为了账号安全,请及时绑定邮箱和手机立即绑定

正在获取“恐慌:os:在使用O_APPEND打开的文件上无效使用 WriteAt”

正在获取“恐慌:os:在使用O_APPEND打开的文件上无效使用 WriteAt”

Go
ibeautiful 2022-09-19 10:10:21
我是Go的新手。我开始编写我的第一个代码,我必须从AWS下载一堆CSV。我不明白为什么它给我以下错误与O_APPEND模式。如果我删除 ,我只得到最后一个文件数据,这不是目标。os.O_APPEND目标是将所有 CSV 文件下载到本地的一个文件中。我想了解我做错了什么。package mainimport (    "fmt"    "os"    "path/filepath"    "github.com/aws/aws-sdk-go/aws"    "github.com/aws/aws-sdk-go/aws/credentials"    "github.com/aws/aws-sdk-go/aws/session"    "github.com/aws/aws-sdk-go/service/s3"    "github.com/aws/aws-sdk-go/service/s3/s3manager")const (    AccessKeyId     = "xxxxxxxxx"    SecretAccessKey = "xxxxxxxxxxxxxxxxxxxx"    Region          = "eu-central-1"    Bucket          = "dexter-reports"    bucketKey       = "Jenkins/pluginVersions/")func main() {    // Load the Shared AWS Configuration    os.Setenv("AWS_ACCESS_KEY_ID", AccessKeyId)    os.Setenv("AWS_SECRET_ACCESS_KEY", SecretAccessKey)    filename := "JenkinsPluginDetais.txt"    cred := credentials.NewStaticCredentials(AccessKeyId, SecretAccessKey, "")    config := aws.Config{Credentials: cred, Region: aws.String(Region), Endpoint: aws.String("s3.amazonaws.com")}    file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)    if err != nil {        panic(err)    }    defer file.Close()    sess, err := session.NewSession(&config)    if err != nil {        fmt.Println(err)    }    //list Buckets    ObjectList := listBucketObjects(sess)    //loop over the obectlist. First initialize the s3 downloader via s3manager    downloader := s3manager.NewDownloader(sess)    for _, item := range ObjectList.Contents {        csvFile := filepath.Base(*item.Key)        if csvFile != "pluginVersions" {            downloadBucketObjects(downloader, file, csvFile)        }    }}func listBucketObjects(sess *session.Session) *s3.ListObjectsV2Output {    //create a new s3 client    svc := s3.New(sess)    resp, err := svc.ListObjectsV2(&s3.ListObjectsV2Input{        Bucket: aws.String(Bucket),        Prefix: aws.String(bucketKey),    })    if err != nil {        panic(err)    }    return resp}
查看完整描述

2 回答

?
回首忆惘然

TA贡献1847条经验 获得超11个赞

首先,我不明白为什么你首先需要旗帜。根据我的理解,您可以省略.os.O_APPENDos.O_APPEND


现在,让我们来看看为什么会发生这种情况的实际问题:


文档 (参考: https://man7.org/linux/man-pages/man2/open.2.html):O_APPEND


O_APPEND

              The file is opened in append mode.  Before each write(2),

              the file offset is positioned at the end of the file, as

              if with lseek(2).  The modification of the file offset and

              the write operation are performed as a single atomic step.

因此,对于每个对文件的调用,偏移量都位于文件的末尾。write


但据说使用的是方法,即(*s3Manager.Download).DownloadWriteAt


文档 :WriteAt


$ go doc os WriteAt


package os // import "os"


func (f *File) WriteAt(b []byte, off int64) (n int, err error)

    WriteAt writes len(b) bytes to the File starting at byte offset off. It

    returns the number of bytes written and an error, if any. WriteAt returns a

    non-nil error when n != len(b).


    If file was opened with the O_APPEND flag, WriteAt returns an error.

请注意最后一行,如果文件是用标志打开的,它将导致错误,它甚至是正确的,因为 WriteAt 的第二个参数是偏移量,但混合的行为和偏移量搜索可能会产生问题,导致意外的结果,并且它出错了。O_APPENDO_APPENDWriteAt


查看完整回答
反对 回复 2022-09-19
?
明月笑刀无情

TA贡献1828条经验 获得超4个赞

考虑 s3 管理器的定义。下载器:


func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ...func(*Downloader)) (n int64, err error)

第一个参数是 ;此接口是:io.WriterAt


type WriterAt interface {

    WriteAt(p []byte, off int64) (n int, err error)

}

这意味着该函数将调用您正在传递的方法中的方法。根据文件.写在的文档DownloadWriteAtFile


如果使用O_APPEND标志打开文件,则 WriteAt 将返回错误。


因此,这解释了为什么您会收到错误,但提出了一个问题“为什么使用和不接受(和调用)?答案可以在文档中找到:DownloadWriteAtio.WriterWrite


w io.作者可以通过操作系统来满足。用于执行多部分并发下载的文件,或使用 aws 在内存 [] 字节包装器中。写入缓冲区


因此,为了提高性能,可能会对文件的某些部分发出多个同时请求,然后在收到这些请求时将其写出(这意味着它可能不会按顺序写入数据)。这也解释了为什么多次调用函数时,使用相同的结果覆盖数据(当检索文件的每个块时,它会在输出文件中的适当位置将其写出;这将覆盖已经存在的任何数据)。DownloaderFileDownloader


文档中的上述引用也指出了一个可能的解决方案;使用 aws。WriteAtBuffer,下载完成后,将数据写入您的文件(然后可以使用 打开该文件) - 如下所示:O_APPEND


buf := aws.NewWriteAtBuffer([]byte{})

numBytes, err := downloader.Download(buf,

        &s3.GetObjectInput{

            Bucket: aws.String(Bucket),

            Key:    aws.String(fileToDownload),

        })


if err != nil {

    panic(err)

}

_, err = file.Write(buf.Bytes())

if err != nil {

    panic(err)

}

另一种方法是下载到临时文件中,然后将其附加到输出文件中(如果文件很大,则可能需要执行此操作)。


查看完整回答
反对 回复 2022-09-19
  • 2 回答
  • 0 关注
  • 63 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信