Categorygithub.com/nguyengg/go-aws-commons/s3reader
modulepackage
0.1.1
Repository: https://github.com/nguyengg/go-aws-commons.git
Documentation: pkg.go.dev

# README

Implements io.ReadSeeker, io.ReaderAt, and io.WriterTo using S3 ranged GetObject

Go Reference

This module provides implementations of io.ReadSeeker, io.ReaderAt, and io.WriterTo for S3 downloading needs.

Get with:

go get github.com/nguyengg/go-aws-commons/s3reader
package main

import (
	"context"
	"io"
	"log"
	"os"
	"os/signal"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/krolaw/zipstream"
	"github.com/nguyengg/go-aws-commons/s3reader"
	"github.com/nguyengg/xy3/zipper"
)

func main() {
	ctx, stop := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt)
	defer stop()

	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		log.Fatal(err)
	}

	client := s3.NewFromConfig(cfg)

	// s3reader.Reader implements both io.ReadSeeker and io.ReaderAt so I can start streaming the
	// S3 object however I want.
	// if in interactive mode, s3reader.WithProgressBar will show a progress bar displaying progress.
	// otherwise, use s3reader.WithProgressLogger instead.
	reader, err := s3reader.New(ctx, client, &s3.GetObjectInput{
		Bucket: aws.String("my-bucket"),
		Key:    aws.String("my-key"),
	}, s3reader.WithProgressBar())
	if err != nil {
		log.Fatal(err)
	}

	// for example, if reader is a ZIP file, I can use xy3 to extract the zip file headers
	// without reading the whole file.
	cd, err := zipper.NewCDScanner(reader, reader.Size())
	if err != nil {
		log.Fatal(err)
	}
	for fh := range cd.All() {
		// fh is a zipper.CDFileHeader which embeds zip.FileHeader.
		// in theory, with the offset, I should be able to use reader.ReadAt to find
		// the local file header and perform parallel decompression on each file in
		// the archive.
		log.Printf("%s can be found at offset=%d", fh.Name, fh.Offset)
	}

	// In this example, zipstream is used instead to stream the entire file.
	// I do need to either reset the reader with Seek, or create a new one using Reopen.
	for zr := zipstream.NewReader(reader.Reopen()); ; {
		fh, err := zr.Next()
		if err != nil {
			log.Fatal(err)
		}

		// zr implements Reader as well so extract the file like this.
		f, err := os.Create(fh.Name)
		if err == nil {
			_, err = io.Copy(f, zr)
			_ = f.Close()
		}
		if err != nil {
			log.Fatal(f)
		}
	}
}

# Functions

New returns a Reader with the given GetObject input parameters.
NewReaderWithSize returns a Reader with the given GetObject input parameters and known size.
WithProgressBar adds a progress bar that displays download progress.
WithProgressLogger adds a progress logger that logs download progress with the given interval.

# Constants

DefaultBufferSize is the default value for Options.BufferSize.
DefaultConcurrency is the default value for Options.Concurrency.
DefaultPartSize is the default value for Options.PartSize.
DefaultThreshold is the default value for GetOptions.Threshold.

# Variables

ErrClosed is returned by all Reader read methods after Close returns.
ErrSeekBeforeFirstByte is returned by Reader.Seek if the parameters would end up moving the internal read offset to a negative number.
ErrSeekPastLastByte is returned by Reader.Seek if the parameters would end up moving the internal read offset past the offset of the last byte (Reader.Size-1).

# Structs

Options customises the returned Reader of New and NewReaderWithSize.

# Interfaces

GetAndHeadObjectClient abstracts the S3 APIs that are needed for New to determine the object size.
GetObjectClient abstracts the S3 APIs that are needed to implement Reader.
Reader uses ranged GetObject to implement io.ReadSeekCloser, io.ReaderAt, and io.WriterAt.