iocopy

package module
v1.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2024 License: MIT Imports: 5 Imported by: 2

README

iocopy

iocopy is a Golang package which provides functions to do IO copy in a new goroutine and returns an event channel for the caller.

Features

  • Asynchronous io copy operation
  • Caller can read the event channel to receive the events generated by the worker goroutine
  • Context aware

Docs

License

Documentation

Index

Examples

Constants

View Source
const (
	// DefBufSize is the default buffer size.
	DefBufSize = uint(32 * 1024)

	// DefaultInterval is the default interval to report count of written bytes.
	DefaultInterval = 500 * time.Millisecond
)

Variables

This section is empty.

Functions

func Start

func Start(
	ctx context.Context,
	dst io.Writer,
	src io.Reader,
	bufSize uint,
	total uint64,
	interval time.Duration) <-chan Event

Start returns a channel for the caller to receive IO copy events and start a goroutine to do IO copy. ctx: context.Context. It can be created using context.WithCancel, context.WithDeadline, context.WithTimeout... dst: io.Writer to copy to. src: io.Reader to copy from. bufSize: size of the buffer. It'll create a buffer in the new goroutine according to the buffer size. total: total number of bytes to copy. Set it to 0 if it's unknown. interval: It'll create a time.Ticker by given interval to send the EventWritten event to the channel during the IO copy. A negative or zero duration causes it to stop the ticker immediately. In this case, it'll send the EventWritten to the channel only once when IO copy succeeds. You may set it to DefaultInterval.

It returns a channel to receive IO copy events. Available events:

(1). n bytes have been written successfully. It'll send an EventWritten to the channel.

(2). an error occured It'll send an EventError to the channel and close the channel.

(3). IO copy stopped(context is canceled or context's deadline exceeded). It'll send an EventStop to the channel and close the channel.

(4). IO copy succeeded. It'll send an EventOK to the channel and close the channel.

You may use a for-range loop to read events from the channel.

Example
package main

import (
	"context"
	"crypto/sha256"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/northbright/iocopy"
)

func main() {
	// Example of iocopy.Start()
	// It reads a remote file and calculates its SHA-256 hash.
	// It shows how to read events and process them from the event channel.

	// URL of remote file.
	// SHA-256: 9e2f2a4031b215922aa21a3695e30bbfa1f7707597834287415dbc862c6a3251
	downloadURL := "https://golang.google.cn/dl/go1.20.1.darwin-amd64.pkg"

	// Do HTTP request and get the response.
	resp, err := http.Get(downloadURL)
	if err != nil {
		log.Printf("http.Get() error: %v", err)
		return
	}
	// Check status code.
	if resp.StatusCode != 200 && resp.StatusCode != 206 {
		log.Printf("status code is not 200 or 206")
		return
	}

	// response.Body is an io.ReadCloser.
	// Do not forget to close the body.
	defer resp.Body.Close()

	// Get remote file size.
	contentLength := resp.Header.Get("Content-Length")
	total, _ := strconv.ParseUint(contentLength, 10, 64)
	if total <= 0 {
		log.Printf("Content-Length <= 0: %v", total)
		return
	}

	// Create a hash.Hash for SHA-256.
	// hash.Hash is an io.Writer.
	hash := sha256.New()

	// create a context.
	// It can be created by context.WithCancel, context.WithDeadline,
	// context.WithTimeout...
	// You may test timeout context.
	// The IO copy will be stopped
	// and an EventStop will be sent to the channel.
	// event.Err() will return "context deadline exceeded".
	// ctx, cancel := context.WithTimeout(context.Background(), 1200*time.Millisecond)
	// defer cancel()

	// Use background context by default.
	// IO copy should succeed and an EventOK will be sent to the channel.
	ctx := context.Background()

	// Start a goroutine to do IO copy.
	// Read from response.Body and write to hash.Hash to compute hash.
	ch := iocopy.Start(
		// Context
		ctx,
		// Writer(dst)
		hash,
		// Reader(src)
		resp.Body,
		// Buffer size
		16*1024*1024,
		// Total size, set it to 0 if it's unknown.
		total,
		// Interval to report written bytes
		500*time.Millisecond)

	// Read the events from the channel.
	for event := range ch {
		switch ev := event.(type) {
		case *iocopy.EventWritten:
			// n bytes have been written successfully.
			// Get the count of bytes.
			n := ev.Written()
			percent := ev.Percent()
			log.Printf("on EventWritten: %v/%v bytes written(%.2f%%)", n, total, percent)

		case *iocopy.EventStop:
			// Context is canceled or
			// context's deadline exceeded.
			// Get EventWritten from EventStop.
			ew := ev.EventWritten()

			// Get the number of written bytes and percent.
			n := ew.Written()
			percent := ew.Percent()

			log.Printf("on EventStop: %v, %v/%v bytes written(%.2f%%)", ev.Err(), n, total, percent)

		case *iocopy.EventError:
			// an error occured.
			// Get the error.
			log.Printf("on EventError: %v", ev.Err())

		case *iocopy.EventOK:
			// IO copy succeeded.
			// Get EventWritten from EventOK.
			ew := ev.EventWritten()

			// Get the number of written bytes and percent.
			n := ew.Written()
			percent := ew.Percent()
			log.Printf("on EventOK: %v/%v bytes written(%.2f%%)", n, total, percent)

			// Get the final SHA-256 checksum of the remote file.
			checksum := hash.Sum(nil)
			fmt.Printf("SHA-256:\n%x", checksum)
		}
	}

	// The event channel will be closed after:
	// (1). iocopy.EventError received.
	// (2). iocopy.EventStop received.
	// (3). iocopy.EventOK received.
	// The for-range loop exits when the channel is closed.
	log.Printf("IO copy gouroutine exited and the event channel is closed")

}
Output:

SHA-256:
9e2f2a4031b215922aa21a3695e30bbfa1f7707597834287415dbc862c6a3251

Types

type Event

type Event interface {
	// stringer
	String() string
}

Event is the interface that wraps String method. When Start is called, it'll return a channel for the caller to receive IO copy related events. Currently, there're 4 types of events: (1). EventWritten - n bytes have been written successfully. (2). EventError - an error occurs and the goroutine exits. (3). EventStop - IO copy stopped. (4). EventOK - IO copy succeeded.

type EventError

type EventError struct {
	// contains filtered or unexported fields
}

EventError is the event that an error occurs.

func (*EventError) Err

func (e *EventError) Err() error

Err returns the error occured during IO copy.

func (*EventError) String

func (e *EventError) String() string

String implements the stringer interface.

type EventOK

type EventOK struct {
	// contains filtered or unexported fields
}

EventOK is the event that IO copy succeeded.

func (*EventOK) EventWritten added in v1.6.0

func (e *EventOK) EventWritten() *EventWritten

EventWritten returns the contained EventWritten event, which can be used to call Written, Total, Percent method on it.

func (*EventOK) String

func (e *EventOK) String() string

String implements the stringer interface.

type EventStop added in v1.1.0

type EventStop struct {
	// contains filtered or unexported fields
}

EventOK is the event that IO copy stopped.

func (*EventStop) Err added in v1.1.0

func (e *EventStop) Err() error

Err returns the the context error that explains why IO copying is stopped. It can be context.Canceled or context.DeadlineExceeded.

func (*EventStop) EventWritten added in v1.6.0

func (e *EventStop) EventWritten() *EventWritten

EventWritten returns the contained EventWritten event, which can be used to call Written, Total, Percent method on it.

func (*EventStop) String added in v1.1.0

func (e *EventStop) String() string

String implements the stringer interface.

type EventWritten

type EventWritten struct {
	// contains filtered or unexported fields
}

EventWritten is the event that n bytes have been written successfully.

func (*EventWritten) Percent added in v1.6.0

func (e *EventWritten) Percent() float32

Percent returns the percentage of the progress. It's always 0 when total is 0.

func (*EventWritten) String

func (e *EventWritten) String() string

String implements the stringer interface.

func (*EventWritten) Total added in v1.6.0

func (e *EventWritten) Total() uint64

Total returns the total number of bytes to copy. It's the same as total argument of Start.

func (*EventWritten) Written

func (e *EventWritten) Written() uint64

Written returns the number of bytes written successfuly.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL