Добавить данные в объект S3

Допустим, у меня есть машина, которую я хочу записать в определенный файл журнала, хранящийся в корзине S3.

Итак, у машины должны быть возможности записи в это ведро, но я не хочу, чтобы у нее была возможность перезаписывать или удалять любые файлы в этом ведре (включая тот, в который я хочу записать).

Поэтому я хочу, чтобы моя машина могла только добавлять данные в этот файл журнала, не переопределяя и не загружая их.

Есть ли способ настроить мой S3 для такой работы? Может быть, есть какая-то политика IAM, которую я могу прикрепить к ней, чтобы она работала, как я хочу?

11 ответов

Решение

К сожалению, вы не можете.

S3 не имеет операции добавления.* После того, как объект был загружен, нет возможности изменить его на месте; единственный вариант - загрузить новый объект, чтобы заменить его, который не соответствует вашим требованиям.

*: Да, я знаю, что этому посту пару лет. Это все еще точно, хотя.

Как говорится в принятом ответе, вы не можете. Лучшее решение, которое я знаю, это использовать:

AWS Kinesis Firehose

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

Их пример кода выглядит сложным, но ваш может быть очень простым. Вы продолжаете выполнять операции PUT (или BATCH PUT) над потоком доставки Kinesis Firehose в своем приложении (используя AWS SDK) и настраиваете поток доставки Kinesis Firehose для отправки потоковых данных в выбранную вами корзину AWS S3 (в Консоль AWS Kinesis Firehose).

Это все еще не так удобно, как >> из командной строки Linux, потому что, как только вы создали файл на S3, вам снова придется иметь дело с загрузкой, добавлением и загрузкой нового файла, но вам нужно делать это только один раз для пакета строк, а не для каждой строки данных поэтому вам не нужно беспокоиться об огромных расходах из-за объема операций добавления. Может быть, это можно сделать, но я не вижу, как это сделать с консоли.

Объекты на 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
    }); 
}

Вы можете:

  1. Настроить многостраничную загрузку
  2. Вызовите UploadPartCopy, указав существующий объект s3 в качестве источника
  3. Вызовите UploadPart с данными, которые вы хотите добавить.
  4. Закройте многостраничную загрузку.

Существует ряд ограничений, например, ваш существующий объект должен быть больше 5 МБ (однако, если он меньше, копирование его клиенту должно быть достаточно быстрым в большинстве случаев). Это не так хорошо, как прямое добавление, но, по крайней мере, вам не нужно для копирования данных туда и обратно с AWS на локальный компьютер.

Если кто-то хочет добавить данные к объекту с помощью службы, подобной S3, Alibaba Cloud OSS (служба хранилища объектов) поддерживает это изначально.

OSS обеспечивает загрузку с добавлением (через API-интерфейс AppendObject), что позволяет напрямую добавлять контент в конец объекта. Объекты, загруженные с помощью этого метода, являются добавляемыми объектами, тогда как объекты, загруженные с помощью других методов, являются обычными объектами. Добавленные данные мгновенно читаются.

Проблема, с которой мы столкнулись, заключалась в создании большого файла s3 размером в несколько гигабайт, причем он никогда не помещался целиком в оперативную память. Приведенный ниже подход объединяет несколько файлов, добавляя их в конец друг друга, поэтому в зависимости от ваших потребностей это может быть жизнеспособным решением.

Решение, которое мы придумали, было:

  1. Загрузите файл фрагментами в папку AWS S3.
  2. Используйте AWS Athena, чтобы определить таблицу на основе этой папки S3, запустив
      CREATE EXTERNAL TABLE IF NOT EXISTS `TrainingDB`.`TrainingTable` (`Data` string)
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe'
WITH SERDEPROPERTIES ('collection.delim' = '\n')
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://your-bucket-name/TrainingTesting/';

  1. Создайте комбинацию всех результатов в этой таблице, запустив
      UNLOAD (SELECT * FROM "TrainingDB"."TrainingTable") 
TO 's3://your-bucket/TrainingResults/results5' 
WITH ( format = 'TEXTFILE', compression='none' )

это добавит все файлы друг к другу и предоставит вам один файл со всеми фрагментами, которые вы пытались добавить. Это излишне, если вы просто пытаетесь объединить несколько небольших файлов, и в этом случае, вероятно, будет лучше просто вытащить исходный файл и записать его в конец (как предполагают другие ответы)

У меня была похожая проблема, и это то, что я спросил

как добавить данные в файл с помощью 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');
     });
}

Надеюсь, это поможет!

Как уже указывалось ранее, объекты S3 недоступны для добавления.
Однако другим решением было бы записать в журналы CloudWatch и затем экспортировать журналы, которые вы хотите, в S3. Это также предотвратит удаление злоумышленниками, обращающимися к вашему серверу, из вашей корзины S3, поскольку Lambda не потребует каких-либо разрешений S3.

Ведро S3 не позволяет вам добавлять существующие объекты, способ, который можно использовать для этого, - сначала использовать метод get для получения данных из корзины S3, затем добавить новые данные, которые вы хотите добавить в него локально, а затем нажать их вернемся к ведру S3.

As, Невозможно добавить к существующему объекту S3. Вам нужно будет заменить его новым объектом с добавленными к нему данными. Это означает, что вам нужно будет загружать весь объект (файл журнала) каждый раз, когда к нему добавляется новая запись. Это будет не очень эффективно.

Вы могли бы иметь записи журнала, отправленные в очередь SQS, и когда размер очереди достигнет установленного числа, вы могли бы объединить сообщения журнала вместе и добавить как объект в вашу корзину S3. Это по-прежнему не удовлетворит ваше требование добавления к одному объекту

У меня была аналогичная проблема, когда мне приходилось записывать ошибки в файл журнала в S3 во время длительного процесса (несколько часов). Поэтому у меня не было локального файла для создания одноразового потока, но мне приходилось добавлять ошибки в файл во время выполнения.

Итак, что вы можете сделать, так это сохранить открытое соединение с определенным файлом и писать в файл, когда хотите:

      const { S3 } = require('aws-sdk')
const { PassThrough } = require('stream')

// append to open connection
const append = (stream, data ) => new Promise(resolve => {
  stream.write(`${data}\n`, resolve)
})

const openConnectionWithS3 = async () => {
  const s3 = new S3({
    credentials: {
      accessKeyId: process.env.AWS_ACCESS_KEY_ID,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    },
    endpoint: process.env.AWS_S3_ENDPOINT,
    region: process.env.AWS_DEFAULT_REGION,
  })
  const fileName = 'test.log'
  const bucketName = 'my-bucket'
  // create pass through stream. This stream we use to write data to
  // but this stream we also use to pass the same data to aws
  const pass = new PassThrough()

  // dont resolve the promise, but keep it open and await for the result when the long running process is done
  const promise = s3
    .upload({
      Bucket: bucketName,
      Key: fileName,
      // pass the stream as body, aws will handle the stream from now
      Body: pass,
    })
    .promise()

  // write data to our open connection.
  // we can even write it on different places
  for (let i = 0; i < 100000; i++) {
    await append(pass, `foo${i}`)
  }

  // here we resolve the promise and close the connection
  await Promise.all([
    // push null to the stream, the stream now knows after the
    // 1000 foo's it should stop writing
    pass.push(null),
    promise,
  ])
}

openConnectionWithS3()

Он добавит элементы в файл в S3 и разрешит, когда это будет сделано.

Да, можете, с s3fs.

      import s3fs

s3 = s3fs.S3FileSystem(anon=False)

# Create a file just like you do on a local system
path_to_your_file = "s3://my-bucket/my-key/my_file.txt

with s3.open('path_to_your_file, 'w') as f:
    f.write(f"This is a new QA file!\n")

# Now append to the file just like you do on a local system.
with s3.open('path_to_your_file, 'a') as f:
    f.write(f"----------------------------------------------------------!\n")

Если вы проверите файл на s3, вы увидите добавленную пунктирную линию. Вы должны настроить s3fs для работы с вашим локальным (инструментами CLI).

Я надеюсь, что это помогает!

Другие вопросы по тегам