Добавить данные в объект 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 решения в этом случае:
- скопируйте все данные 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 } }); } }); }
- Второй вариант - использовать 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 }); }
Вы можете:
- Настроить многостраничную загрузку
- Вызовите UploadPartCopy, указав существующий объект s3 в качестве источника
- Вызовите UploadPart с данными, которые вы хотите добавить.
- Закройте многостраничную загрузку.
Существует ряд ограничений, например, ваш существующий объект должен быть больше 5 МБ (однако, если он меньше, копирование его клиенту должно быть достаточно быстрым в большинстве случаев). Это не так хорошо, как прямое добавление, но, по крайней мере, вам не нужно для копирования данных туда и обратно с AWS на локальный компьютер.
Если кто-то хочет добавить данные к объекту с помощью службы, подобной S3, Alibaba Cloud OSS (служба хранилища объектов) поддерживает это изначально.
OSS обеспечивает загрузку с добавлением (через API-интерфейс AppendObject), что позволяет напрямую добавлять контент в конец объекта. Объекты, загруженные с помощью этого метода, являются добавляемыми объектами, тогда как объекты, загруженные с помощью других методов, являются обычными объектами. Добавленные данные мгновенно читаются.
Проблема, с которой мы столкнулись, заключалась в создании большого файла s3 размером в несколько гигабайт, причем он никогда не помещался целиком в оперативную память. Приведенный ниже подход объединяет несколько файлов, добавляя их в конец друг друга, поэтому в зависимости от ваших потребностей это может быть жизнеспособным решением.
Решение, которое мы придумали, было:
- Загрузите файл фрагментами в папку AWS S3.
- Используйте 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/';
- Создайте комбинацию всех результатов в этой таблице, запустив
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).
Я надеюсь, что это помогает!