将数据附加到S3对象


91

假设我有一台想要写入存储在S3存储桶中的特定日志文件的机器。

因此,机器需要对该存储桶具有写入功能,但是,我不希望它具有覆盖或删除该存储桶中任何文件(包括我要写入的文件)的能力。

因此,基本上,我希望我的机器只能将数据追加到该日志文件,而不会覆盖或下载该文件。

有没有一种方法可以配置S3使其正常工作?也许我可以附加一些IAM策略,以使其按我的意愿工作?


您无法在S3中修改对象。您可以追加一个新的日志文件吗?那将是一个更好的模型,并且将支持多个同时客户端。
jarmod

@jarmod是的,我曾想过,但是问题是,如果攻击者成功访问了我的服务器,他将能够删除存储在其上的本地文件,然后再将其发送到S3存储桶(这就是说会在一天结束时发生)。
西奥多

您可能还想看看CloudWatch日志。让它管理收集和存储日志的复杂性,提供搜索工具,保留策略,并允许您根据可为日志自定义的指标生成警报。
jarmod

1
您也可以看看Google BigQuery。您可以使用它来解决您的问题。
Daniel777 '17

Answers:


133

不幸的是,你不能。

S3没有“追加”操作。*对象上传后,就无法对其进行修改;您唯一的选择是上传一个新对象来替换它,这不符合您的要求。

*:是的,我知道这个帖子已经有两年历史了。不过,它仍然是准确的。


我可以知道,通过使用分段上传可以实现这一目标吗?
安贾利

1
分段上传允许您将数据获取到S3中,而无需下载原始对象,但是它不允许您直接覆盖原始对象。参见例如docs.aws.amazon.com/AmazonS3/latest/API / ...。然后,您可以删除旧对象/重命名新对象。但是,这不是问题要问的。
MikeGM '18

我认为使用分段上传可能确实有效。您的所有零件都是同一文件的顺序段。如果零件成功上传,您最终可以提交上传以读取文件。因此,只要您不需要读取文件的内容,就可以追加使用相同的分段上传。
cerebrotecnologico

@cerebrotecnologico我仍然认为它不符合OP的要求。我没有办法限制S3用户执行附加到对象的分段上传-如果他们可以执行分段上传,则可以上传所需的任何内容。
黄昏

16

如公认的答案所述,您不能这样做。我知道的最佳解决方案是使用:

AWS Kinesis Firehose

https://aws.amazon.com/kinesis/firehose/

他们的代码示例看起来很复杂,但是您的示例可能非常简单。您继续对应用程序中的Kinesis Firehose交付流执行PUT(或BATCH PUT)操作(使用AWS开发工具包),并配置Kinesis Firehose交付流将流式数据发送到您选择的AWS S3存储桶(在AWS Kinesis Firehose控制台)。

在此处输入图片说明

它仍然不像>>Linux命令行那样方便,因为一旦在S3上创建了文件,您就不得不再次处理新文件的下载,附加和上载,但是您只需要每行执行一次即可,而不是而不是每行数据,因此您不必担心由于追加操作的数量而产生的巨额费用。也许可以做到,但是我看不到如何从控制台上做到。


8
请注意,执行此操作的时间最长(自创建文件以来为900秒)或最大大小(文件大小为128mb),这意味着Kinesis firehose将附加到同一S3文件,直到达到以下两个限制之一:docs.aws .amazon.com / firehose / latest / dev / create-configure.html
Yaron Budowski

您可以在Firehose上使用单个S3文件作为输出吗?必须在S3存储桶中合并多个文件听起来有些混乱。
约翰·特劳斯提·阿拉森(JónTrausti Arason),

1
很不幸的是,不行。我也希望有更好的解决方案。
Sridhar Sarnobat

是的,这很不幸。如果我手动将记录下载并附加到单个S3对象,则我最担心比赛条件。我一直在考虑将记录添加到SQS,然后对SNS + Lambda使用某种逻辑来轮询SQS,然后将新条目写入S3对象。
约翰·特劳斯提·阿拉森(JónTrausti Arason),

6

S3上的对象不可追加。在这种情况下,您有2个解决方案:

  1. 将所有S3数据复制到新对象,附加新内容并写回S3。
function writeToS3(input) {
    var content;
    var getParams = {
        Bucket: 'myBucket', 
        Key: "myKey"
    };

    s3.getObject(getParams, function(err, data) {
        if (err) console.log(err, err.stack);
        else {
            content = new Buffer(data.Body).toString("utf8");
            content = content + '\n' + new Date() + '\t' + input;
            var putParams = {
                Body: content,
                Bucket: 'myBucket', 
                Key: "myKey",
                ACL: "public-read"
             };

            s3.putObject(putParams, function(err, data) {
                if (err) console.log(err, err.stack); // an error occurred
                else     {
                    console.log(data);           // successful response
                }
             });
        }
    });  
}
  1. 第二种选择是使用Kinesis Firehose。这很简单。您需要创建流水线传送流,并将目标链接到S3存储桶。而已!
function writeToS3(input) {
    var content = "\n" + new Date() + "\t" + input;
    var params = {
      DeliveryStreamName: 'myDeliveryStream', /* required */
      Record: { /* required */
        Data: new Buffer(content) || 'STRING_VALUE' /* Strings will be Base-64 encoded on your behalf */ /* required */
      }
    };

    firehose.putRecord(params, function(err, data) {
      if (err) console.log(err, err.stack); // an error occurred
      else     console.log(data);           // successful response
    }); 
}

您可以使用一个S3文件作为输出吗?
约翰·特劳斯提·阿拉森(JónTrausti Arason),


1

如果有人想通过类似S3的服务将数据附加到对象,则阿里云OSS(对象存储服务)本身就支持此功能

OSS提供了附加上传(通过AppendObject API),它允许您直接将内容附加到对象的末尾。使用此方法上载的对象是可附加对象,而使用其他方法上载的对象是普通对象。附加数据可立即读取。


-1

我有类似的问题,这就是我所问的

如何使用AWS Lambda附加文件中的数据

这是我想出的解决以上问题的方法:

使用getObject从现有文件中检索

   s3.getObject(getParams, function(err, data) {
   if (err) console.log(err, err.stack); // an error occurred
   else{
       console.log(data);           // successful response
       var s3Projects = JSON.parse(data.Body);
       console.log('s3 data==>', s3Projects);
       if(s3Projects.length > 0) {
           projects = s3Projects;
       }   
   }
   projects.push(event);
   writeToS3(); // Calling function to append the data
});

写函数以追加到文件中

   function writeToS3() {
    var putParams = {
      Body: JSON.stringify(projects),
      Bucket: bucketPath, 
      Key: "projects.json",
      ACL: "public-read"
     };

    s3.putObject(putParams, function(err, data) {
       if (err) console.log(err, err.stack); // an error occurred
       else     console.log(data);           // successful response
        callback(null, 'Hello from Lambda');
     });
}

希望这个帮助!


13
您的writeToS3函数将覆盖文件,而不是附加文件。
duskwuff -inactive-

@ duskwuff-inactive-同意,并且如果两种方法尝试在同一个对象上工作,它也会遭受竞争条件的影响,但这与具有不可变字符串或类型的语言并没有真正的区别-您可以通过返回/覆盖来模拟附加一个新对象。
fatal_error
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.