Buffer implementing io.WriterAt in go

huangapple go评论87阅读模式
英文:

Buffer implementing io.WriterAt in go

问题

我正在使用aws-sdk从S3存储桶下载文件。S3下载函数需要实现io.WriterAt接口的对象,但是bytes.Buffer并没有实现该接口。目前我正在创建一个实现io.WriterAt接口的文件,但我希望能够在内存中完成。

英文:

I'm using the aws-sdk to download a file from an s3 bucket. The S3 download function want's something that implements io.WriterAt however bytes.Buffer doesn't implement that. Right now I'm creating a file which implements io.WriterAt but I'd like something in memory.

答案1

得分: 79

对于涉及AWS SDK的情况,请使用aws.WriteAtBuffer将S3对象下载到内存中。

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

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

fmt.Printf("已下载 %v 字节", len(buf.Bytes()))
英文:

For cases involving the AWS SDK, use aws.WriteAtBuffer to download S3 objects into memory.

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()))

答案2

得分: 17

使用 io.Writer 模拟 AWS 的 WriterAt

这不是对原问题的直接回答,而是我在这里找到的实际解决方案。这是一个类似的用例,我认为可能会对其他人有所帮助。

AWS 文档定义了这样的约定:如果将 downloader.Concurrency 设置为 1,那么可以确保写操作是按顺序进行的。

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

因此,你可以使用一个 io.Writer 并对其进行包装,以满足 io.WriterAt 的要求,并丢弃不再需要的 offset

type FakeWriterAt struct {
    w io.Writer
}

func (fw FakeWriterAt) WriteAt(p []byte, offset int64) (n int, err error) {
    // 忽略 'offset',因为我们强制进行顺序下载
    return fw.w.Write(p)
}

希望对你有所帮助!

英文:

Faking AWS WriterAt with an io.Writer

This isn't a direct answer to the original question but rather the solution I actually used after landing here. It's a similar use case that I figure may help others.

The AWS documentation defines the contract such that if you set downloader.Concurrency to 1, you get guaranteed sequential writes.

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

Therefore you can take an io.Writer and wrap it to fulfill the io.WriterAt, throwing away the offset that you no longer need:

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

得分: 5

我不知道标准库中是否有这样的方法,但你可以编写自己的缓冲区。

实际上,这并不难...

编辑:我一直在思考这个问题,最后不小心完成了整个事情,希望你喜欢 Buffer implementing io.WriterAt in go

package main

import (
	"errors"
	"fmt"
)

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

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

// WriteBuffer是一个简单的类型,它在内存中实现了io.WriterAt接口。
// 这个类型的零值是一个空的可用缓冲区。
type WriteBuffer struct {
	d []byte
	m int
}

// NewWriteBuffer创建并返回一个具有给定初始大小和最大值的新WriteBuffer。
// 如果最大值<= 0,则表示无限制。
func NewWriteBuffer(size, max int) *WriteBuffer {
	if max < size && max >= 0 {
		max = size
	}
	return &WriteBuffer{make([]byte, size), max}
}

// SetMax设置WriteBuffer的最大容量。
// 如果提供的最大值小于当前容量但大于0,则设置为当前容量;如果小于或等于零,则表示无限制。
func (wb *WriteBuffer) SetMax(max int) {
	if max < len(wb.d) && max >= 0 {
		max = len(wb.d)
	}
	wb.m = max
}

// Bytes返回WriteBuffer的底层数据。只要在WriteBuffer上没有调用其他方法,该值将保持有效。
func (wb *WriteBuffer) Bytes() []byte {
	return wb.d
}

// Shape返回当前WriteBuffer的大小以及如果提供了最大值,则返回最大值。
func (wb *WriteBuffer) Shape() (int, int) {
	return len(wb.d), wb.m
}

func (wb *WriteBuffer) WriteAt(dat []byte, off int64) (int, error) {
	// 范围/合法性检查。
	if int(off) < 0 {
		return 0, errors.New("偏移量超出范围(太小).")
	}
	if int(off)+len(dat) >= wb.m && wb.m > 0 {
		return 0, errors.New("偏移量+数据长度超出范围(太大).")
	}

	// 检查快速扩展路径
	if int(off) == len(wb.d) {
		wb.d = append(wb.d, dat...)
		return len(dat), nil
	}

	// 检查较慢的扩展路径
	if int(off)+len(dat) >= len(wb.d) {
		nd := make([]byte, int(off)+len(dat))
		copy(nd, wb.d)
		wb.d = nd
	}

	// 一旦不需要扩展,只需将字节复制到指定位置。
	copy(wb.d[int(off):], dat)
	return len(dat), nil
}

以上是你提供的代码的翻译。

英文:

I don't know of any way to do this in the standard library, but you can write your own buffer.

It really wouldn't be all that hard...

EDIT: I couldn't stop thinking about this, and I ended up acidentally the whole thing, enjoy Buffer implementing io.WriterAt in go

package main
import (
&quot;errors&quot;
&quot;fmt&quot;
)
func main() {
buff := NewWriteBuffer(0, 10)
buff.WriteAt([]byte(&quot;abc&quot;), 5)
fmt.Printf(&quot;%#v\n&quot;, 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 &lt;= 0 it is unlimited.
func NewWriteBuffer(size, max int) *WriteBuffer {
if max &lt; size &amp;&amp; max &gt;= 0 {
max = size
}
return &amp;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 &lt; len(wb.d) &amp;&amp; max &gt;= 0 {
max = len(wb.d)
}
wb.m = max
}
// Bytes returns the WriteBuffer&#39;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) &lt; 0 {
return 0, errors.New(&quot;Offset out of range (too small).&quot;)
}
if int(off)+len(dat) &gt;= wb.m &amp;&amp; wb.m &gt; 0 {
return 0, errors.New(&quot;Offset+data length out of range (too large).&quot;)
}
// 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) &gt;= 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
}

答案4

得分: 1

我正在为您翻译以下内容:

我正在寻找一种从S3对象直接获取io.ReadCloser的简单方法。无需缓冲响应或减少并发。

import "github.com/aws/aws-sdk-go/service/s3"

[...]

obj, err := c.s3.GetObject(&s3.GetObjectInput{
	Bucket: aws.String("my-bucket"),
	Key:    aws.String("path/to/the/object"),
})
if err != nil {
	return nil, err
}

// obj.Body 是一个 ReadCloser
return obj.Body, nil

希望对您有所帮助!

英文:

I was looking for a simple way to get io.ReadCloser directly from an S3 object. There is no need to buffer response nor reduce concurrency.

import &quot;github.com/aws/aws-sdk-go/service/s3&quot;
[...]
obj, err := c.s3.GetObject(&amp;s3.GetObjectInput{
Bucket: aws.String(&quot;my-bucket&quot;),
Key:    aws.String(&quot;path/to/the/object&quot;),
})
if err != nil {
return nil, err
}
// obj.Body is a ReadCloser 
return obj.Body, nil

答案5

得分: 1

使用aws-sdk-go-v2,代码库提供的示例显示:

// 示例:
// 预先分配内存缓冲区,其中headObject类型为*s3.HeadObjectOutput
buf := make([]byte, int(headObject.ContentLength))
// 使用aws.WriteAtBuffer进行包装
w := s3manager.NewWriteAtBuffer(buf)
// 将文件下载到内存中
numBytesDownloaded, err := downloader.Download(ctx, w, &s3.GetObjectInput{
	Bucket: aws.String(bucket),
	Key:    aws.String(item),
})

然后使用w.Bytes()作为结果。

导入"github.com/aws/aws-sdk-go-v2/feature/s3/manager"和其他所需的组件。

英文:

With aws-sdk-go-v2, the codebase-provided example shows:

// Example:
// pre-allocate in memory buffer, where headObject type is *s3.HeadObjectOutput
buf := make([]byte, int(headObject.ContentLength))
// wrap with aws.WriteAtBuffer
w := s3manager.NewWriteAtBuffer(buf)
// download file into the memory
numBytesDownloaded, err := downloader.Download(ctx, w, &amp;s3.GetObjectInput{
Bucket: aws.String(bucket),
Key:    aws.String(item),
})

Then use w.Bytes() as the result.

Import "github.com/aws/aws-sdk-go-v2/feature/s3/manager" and other needed components

huangapple
  • 本文由 发表于 2017年9月3日 09:18:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/46019484.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定