web-dev-qa-db-fra.com

Buffer implémentant io.WriterAt in go

J'utilise aws-sdk pour télécharger un fichier à partir d'un compartiment s3. La fonction de téléchargement S3 veut quelque chose qui implémente io.WriterAt mais bytes.Buffer ne l'implémente pas. En ce moment, je crée un fichier qui implémente io.WriterAt mais j'aimerais quelque chose en mémoire.

15
Onestay

Pour les cas impliquant le kit AWS SDK, utilisez aws.WriteAtBuffer pour télécharger les objets S3 en mémoire.

requestInput := s3.GetObjectInput{
    Bucket: aws.String(bucket),
    Key:    aws.String(key),
}

buf := aws.NewWriteAtBuffer([]byte{})
downloader.Download(buf, &requestInput)

fmt.Printf("Downloaded %v bytes", len(buf.Bytes()))
32
Sam

Je ne connais aucun moyen de le faire dans la bibliothèque standard, mais vous pouvez écrire votre propre tampon.

Ce ne serait vraiment pas si difficile ...

EDIT: Je ne pouvais pas m'arrêter de penser à ça, et j'ai fini par accidentellement le tout, profitez :)

package main

import (
    "errors"
    "fmt"
)

func main() {
    buff := NewWriteBuffer(0, 10)

    buff.WriteAt([]byte("abc"), 5)
    fmt.Printf("%#v\n", buff)
}

// WriteBuffer is a simple type that implements io.WriterAt on an in-memory buffer.
// The zero value of this type is an empty buffer ready to use.
type WriteBuffer struct {
    d []byte
    m int
}

// NewWriteBuffer creates and returns a new WriteBuffer with the given initial size and
// maximum. If maximum is <= 0 it is unlimited.
func NewWriteBuffer(size, max int) *WriteBuffer {
    if max < size && max >= 0 {
        max = size
    }
    return &WriteBuffer{make([]byte, size), max}
}

// SetMax sets the maximum capacity of the WriteBuffer. If the provided maximum is lower
// than the current capacity but greater than 0 it is set to the current capacity, if
// less than or equal to zero it is unlimited..
func (wb *WriteBuffer) SetMax(max int) {
    if max < len(wb.d) && max >= 0 {
        max = len(wb.d)
    }
    wb.m = max
}

// Bytes returns the WriteBuffer's underlying data. This value will remain valid so long
// as no other methods are called on the WriteBuffer.
func (wb *WriteBuffer) Bytes() []byte {
    return wb.d
}

// Shape returns the current WriteBuffer size and its maximum if one was provided.
func (wb *WriteBuffer) Shape() (int, int) {
    return len(wb.d), wb.m
}

func (wb *WriteBuffer) WriteAt(dat []byte, off int64) (int, error) {
    // Range/sanity checks.
    if int(off) < 0 {
        return 0, errors.New("Offset out of range (too small).")
    }
    if int(off)+len(dat) >= wb.m && wb.m > 0 {
        return 0, errors.New("Offset+data length out of range (too large).")
    }

    // Check fast path extension
    if int(off) == len(wb.d) {
        wb.d = append(wb.d, dat...)
        return len(dat), nil
    }

    // Check slower path extension
    if int(off)+len(dat) >= len(wb.d) {
        nd := make([]byte, int(off)+len(dat))
        copy(nd, wb.d)
        wb.d = nd
    }

    // Once no extension is needed just copy bytes into place.
    copy(wb.d[int(off):], dat)
    return len(dat), nil
}
3
Milo Christiansen

Fake AWS WriterAt avec un io.Writer

Ce n'est pas une réponse directe à la question d'origine mais plutôt la solution que j'ai réellement utilisée après avoir atterri ici. C'est un cas d'utilisation similaire qui, je pense, peut aider les autres.

La documentation AWS définit le contrat de sorte que si vous définissez downloader.Concurrency à 1, vous obtenez des écritures séquentielles garanties.

downloader.Download(FakeWriterAt{w}, s3.GetObjectInput{
    Bucket: aws.String(bucket),
    Key:    aws.String(key),
})
downloader.Concurrency = 1

Vous pouvez donc prendre un io.Writer et envelopper pour remplir le io.WriterAt, jetant le offset dont vous n'avez plus besoin:

type FakeWriterAt struct {
    w io.Writer
}

func (fw FakeWriterAt) WriteAt(p []byte, offset int64) (n int, err error) {
    // ignore 'offset' because we forced sequential downloads
    return fw.w.Write(p)
}
3
CoolAJ86