Как загрузить файлы на Amazon S3 (официальный SDK) размером более 5 МБ (приблизительно)?
Я использую последнюю версию официального Amazon S3 SDK (1.0.14.1) для создания инструмента резервного копирования. Пока все работает правильно, если размер загружаемого файла меньше 5 МБ, но когда какой-либо из файлов превышает 5 МБ, загрузка не выполняется со следующим исключением:
System.Net.WebException: запрос был прерван: запрос был отменен. --- > System.IO.IOException: не удается закрыть поток, пока все байты не будут написано. в System.Net.ConnectStream.CloseInternal(Boolean internalCall, Boolean aborting) --- Конец внутренней трассировки стека исключений --- в Amazon.S3.AmazonS3Client.ProcessRequestError(String actionName, запрос HttpWebRequest, WebException мы, HttpWebResponse errorResponse, String requestAddr, WebHeaderCollection & respHdrs, Тип t) в Amazon.S3.AmazonS3Client.Invoke [Т] (S3Request userRequest) в Amazon.S3.AmazonS3Client.PutObject(PutObjectRequest запрос) в BackupToolkit.S3Module.UploadFile(String sourceFileName, String destinationFileName) в W:\код\AutoBackupTool\BackupToolkit\S3Module.cs: линия 88 в BackupToolkit.S3Module.UploadFiles(String sourceDirectory) в W:\код\AutoBackupTool\BackupToolkit\S3Module.cs: линия 108
Примечание: 5 МБ - это примерно граница отказа, она может быть немного ниже или что-то большее
Я предполагаю, что соединение отключено, и поток автоматически закрывается до завершения загрузки файла.
Я попытался найти способ установить длительный тайм-аут (но я не могу найти вариант в AmazonS3
или AmazonS3Config
).
Любые идеи о том, как увеличить тайм-аут (например, параметр приложения, который я могу использовать) или он не связан с проблемой тайм-аута?
код:
var s3Client = AWSClientFactory.CreateAmazonS3Client(AwsAccessKey, AwsSecretKey);
var putObjectRequest = new PutObjectRequest {
BucketName = Bucket,
FilePath = sourceFileName,
Key = destinationFileName,
MD5Digest = md5Base64,
GenerateMD5Digest = true
};
using (var upload = s3Client.PutObject(putObjectRequest)) { }
Ответы
Ответ 1
Обновленный ответ:
Недавно я обновил один из моих проектов, который использует Amazon AWS.NET SDK (до версии 1.4.1.0), и в этой версии есть два улучшения, которых не было, когда я написал оригинальный ответ здесь.
- Теперь вы можете установить
Timeout
на -1
, чтобы иметь неограниченное ограничение времени для операции put.
- В
PutObjectRequest
теперь есть дополнительное свойство ReadWriteTimeout
, которое может быть установлено (в миллисекундах) на тайм-аут на уровне чтения/записи потока, противоположном всему уровню операции put.
Итак, теперь мой код выглядит так:
var putObjectRequest = new PutObjectRequest {
BucketName = Bucket,
FilePath = sourceFileName,
Key = destinationFileName,
MD5Digest = md5Base64,
GenerateMD5Digest = true,
Timeout = -1,
ReadWriteTimeout = 300000 // 5 minutes in milliseconds
};
Оригинальный ответ:
Мне удалось выяснить ответ...
Перед отправкой вопроса я исследовал AmazonS3
и AmazonS3Config
, но не PutObjectRequest
.
Внутри PutObjectRequest
есть свойство Timeout
(установлено в миллисекундах). Я успешно использовал это для загрузки больших файлов (примечание: установка его на 0 не удаляет тайм-аут, вам нужно указать положительное количество миллисекунд... Я пошел на 1 час).
Это отлично работает:
var putObjectRequest = new PutObjectRequest {
BucketName = Bucket,
FilePath = sourceFileName,
Key = destinationFileName,
MD5Digest = md5Base64,
GenerateMD5Digest = true,
Timeout = 3600000
};
Ответ 2
У меня были схожие проблемы, и я начал использовать класс TransferUtility для выполнения загрузки нескольких частей.
В настоящий момент этот код работает. У меня были проблемы, когда тайм-аут был слишком низким!
var request = new TransferUtilityUploadRequest()
.WithBucketName(BucketName)
.WithFilePath(sourceFile.FullName)
.WithKey(key)
.WithTimeout(100 * 60 * 60 * 1000)
.WithPartSize(10 * 1024 * 1024)
.WithSubscriber((src, e) =>
{
Console.CursorLeft = 0;
Console.Write("{0}: {1} of {2} ", sourceFile.Name, e.TransferredBytes, e.TotalBytes);
});
utility.Upload(request);
Как я набираю это, у меня есть загрузка 4 ГБ, и она уже прошла дальше, чем когда-либо раньше!
Ответ 3
AWS SDK для .NET имеет два основных API для работы с Amazon S3.Both могут загружать большие и маленькие файлы на S3.
1. API низкого уровня:
Низкоуровневый API использует тот же шаблон, который используется для другой службы низкоуровневые API в SDK. Существует клиентский объект, называемый AmazonS3Client, который реализует интерфейс IAmazonS3. Он содержит методы для каждой из сервисных операций, открытых S3.
Пространство имен: Amazon.S3, Amazon.S3.Model
// Step 1 :
AmazonS3Config s3Config = new AmazonS3Config();
s3Config.RegionEndpoint = GetRegionEndPoint();
// Step 2 :
using(var client = new AmazonS3Client(My_AWSAccessKey, My_AWSSecretKey, s3Config) )
{
// Step 3 :
PutObjectRequest request = new PutObjectRequest();
request.Key = My_key;
request.InputStream = My_fileStream;
request.BucketName = My_BucketName;
// Step 4 : Finally place object to S3
client.PutObject(request);
}
2. TransferUtility: (я бы рекомендовал использовать этот API)
TransferUtility работает поверх низкоуровневого API. Для получение объектов в S3, это простой интерфейс для обработки наиболее распространенных применений S3. Самый большой польза приходит от размещения объектов. Например, гостиницы определяет, является ли файл большим и переключается в режим многократной загрузки.
Пространство имен: Amazon.S3.Transfer
// Step 1 : Create "Transfer Utility" (replacement of old "Transfer Manager")
TransferUtility fileTransferUtility =
new TransferUtility(new AmazonS3Client(Amazon.RegionEndpoint.USEast1));
// Step 2 : Create Request object
TransferUtilityUploadRequest uploadRequest =
new TransferUtilityUploadRequest
{
BucketName = My_BucketName,
FilePath = My_filePath,
Key = My_keyName
};
// Step 3 : Event Handler that will be automatically called on each transferred byte
uploadRequest.UploadProgressEvent +=
new EventHandler<UploadProgressArgs>
(uploadRequest_UploadPartProgressEvent);
static void uploadRequest_UploadPartProgressEvent(object sender, UploadProgressArgs e)
{
Console.WriteLine("{0}/{1}", e.TransferredBytes, e.TotalBytes);
}
// Step 4 : Hit upload and send data to S3
fileTransferUtility.Upload(uploadRequest);
Ответ 4
У Ника Рэнделла есть правильная идея по этому поводу, далее к его сообщению здесь еще один пример с некоторыми альтернативными обработками событий и метод получения процента для загруженного файла:
private static string WritingLargeFile(AmazonS3 client, int mediaId, string bucketName, string amazonKey, string fileName, string fileDesc, string fullPath)
{
try
{
Log.Add(LogTypes.Debug, mediaId, "WritingLargeFile: Create TransferUtilityUploadRequest");
var request = new TransferUtilityUploadRequest()
.WithBucketName(bucketName)
.WithKey(amazonKey)
.WithMetadata("fileName", fileName)
.WithMetadata("fileDesc", fileDesc)
.WithCannedACL(S3CannedACL.PublicRead)
.WithFilePath(fullPath)
.WithTimeout(100 * 60 * 60 * 1000) //100 min timeout
.WithPartSize(5 * 1024 * 1024); // Upload in 5MB pieces
request.UploadProgressEvent += new EventHandler<UploadProgressArgs>(uploadRequest_UploadPartProgressEvent);
Log.Add(LogTypes.Debug, mediaId, "WritingLargeFile: Create TransferUtility");
TransferUtility fileTransferUtility = new TransferUtility(ConfigurationManager.AppSettings["AWSAccessKey"], ConfigurationManager.AppSettings["AWSSecretKey"]);
Log.Add(LogTypes.Debug, mediaId, "WritingLargeFile: Start Upload");
fileTransferUtility.Upload(request);
return amazonKey;
}
catch (AmazonS3Exception amazonS3Exception)
{
if (amazonS3Exception.ErrorCode != null &&
(amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") ||
amazonS3Exception.ErrorCode.Equals("InvalidSecurity")))
{
Log.Add(LogTypes.Debug, mediaId, "Please check the provided AWS Credentials.");
}
else
{
Log.Add(LogTypes.Debug, mediaId, String.Format("An error occurred with the message '{0}' when writing an object", amazonS3Exception.Message));
}
return String.Empty; //Failed
}
}
private static Dictionary<string, int> uploadTracker = new Dictionary<string, int>();
static void uploadRequest_UploadPartProgressEvent(object sender, UploadProgressArgs e)
{
TransferUtilityUploadRequest req = sender as TransferUtilityUploadRequest;
if (req != null)
{
string fileName = req.FilePath.Split('\\').Last();
if (!uploadTracker.ContainsKey(fileName))
uploadTracker.Add(fileName, e.PercentDone);
//When percentage done changes add logentry:
if (uploadTracker[fileName] != e.PercentDone)
{
uploadTracker[fileName] = e.PercentDone;
Log.Add(LogTypes.Debug, 0, String.Format("WritingLargeFile progress: {1} of {2} ({3}%) for file '{0}'", fileName, e.TransferredBytes, e.TotalBytes, e.PercentDone));
}
}
}
public static int GetAmazonUploadPercentDone(string fileName)
{
if (!uploadTracker.ContainsKey(fileName))
return 0;
return uploadTracker[fileName];
}
Ответ 5
см. эту тему здесь Как загрузить файл в amazon S3 очень просто, используя С#, включая демонстрационный проект для загрузки.
это высокий уровень с использованием AWS sdk.net 3.5 (и выше), он может быть использован с использованием следующего кода:
// preparing our file and directory names
string fileToBackup = @"d:\mybackupFile.zip" ; // test file
string myBucketName = "mys3bucketname"; //your s3 bucket name goes here
string s3DirectoryName = "justdemodirectory";
string s3FileName = @"mybackupFile uploaded in 12-9-2014.zip";
AmazonUploader myUploader = new AmazonUploader();
myUploader.sendMyFileToS3(fileToBackup, myBucketName, s3DirectoryName, s3FileName);