Amazon S3 客户端无法下载带有空格或散列的文件?
Amazon S3 client could not download file with spaces or hash?
我正在使用 AWSSDK.S3
版本 3.3.17.2
和 AWSSDK.Core
版本 3.3.21.16
上传一个文件,然后下载同一个文件。下面的代码无法下载文件如果文件名有空格(或#
)
public class AmazonS3
{
public async Task<string> UploadFileAsync(string sourceFile, string s3BucketUrl)
{
AmazonS3Uri s3Uri = new AmazonS3Uri(s3BucketUrl);
using (var s3 = new AmazonS3Client(s3Uri.Region))
{
using (TransferUtility utility = new TransferUtility(s3))
{
TransferUtilityUploadRequest request = new TransferUtilityUploadRequest
{
BucketName = s3Uri.Bucket,
ContentType = "application/pdf",
FilePath = sourceFile,
Key = s3Uri.Key + Path.GetFileName(sourceFile),
};
await utility.UploadAsync(request).ConfigureAwait(false);
}
}
return Path.Combine(s3BucketUrl, Path.GetFileName(sourceFile));
}
public async Task DownloadFileAsync(string destinationFilePath, string s3Url)
{
var s3Uri = new AmazonS3Uri(s3Url);
var s3Client = new AmazonS3Client(s3Uri.Region);
GetObjectRequest getObjectRequest = new GetObjectRequest
{
BucketName = s3Uri.Bucket,
Key = s3Uri.Key
};
// dispose the underline stream when writing to local file system is done
using (var getObjectResponse = await s3Client.GetObjectAsync(getObjectRequest).ConfigureAwait(false))
{
await getObjectResponse.WriteResponseStreamToFileAsync(destinationFilePath, false, default(System.Threading.CancellationToken)).ConfigureAwait(false);
}
}
}
然后出于测试目的,我正在上传文件并再次下载相同的文件
AmazonS3 s3 = new AmazonS3();
var uploadedFileS3Link = await s3.UploadFileAsync("C:\temp\my test file.pdf", @"https://mybucket.s3-us-west-2.amazonaws.com/development/test/");
// get exception at line below
await s3.DownloadFileAsync("C:\temp\downloaded file.pdf",uploadedFileS3Link );
我收到异常
Amazon.S3.AmazonS3Exception: The specified key does not exist. --->
Amazon.Runtime.Internal.HttpErrorResponseException: The remote server
returned an error: (404) Not Found. ---> System.Net.WebException: The
remote server returned an error: (404) Not Found. at
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at
System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult
iar, Func
2 endFunction, Action1 endAction, Task
1 promise, Boolean
requiresSynchronization) --- End of stack trace from previous
location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at ......
为简洁起见删除了剩余的异常
存储桶中确实存在该文件。事实上,我可以复制并粘贴 s3url(即 uploadedFileS3Link
变量的值)并通过浏览器下载文件。
(请注意,实际上我正在尝试下载 1000 多个已上传且名称中包含空格的文件。因此在上传时删除空格不是一种选择)
更新 1
我注意到 S3 浏览器 Url 编码文件名
我尝试使用编码文件路径下载文件 https://mybucket.s3-us-west-2.amazonaws.com/development/test/my%20test%20file.pdf
但还是不行
所以我终于找到了问题所在。我正在使用 AmazonS3Uri class 来解析给定的 S3 url 并获取密钥、存储桶和区域。 AmazonS3Uri
return 是我的密钥 development/test/my%20test%20file.pdf
因为在内部 AmazonS3Uri
正在使用 System.Uri
构建 Uri 然后 returns AbsolutePath
其中 returns 编码路径作为 Key (它应该 return 本地路径作为键吗?)
我不知道为什么 AmazonS3Client
不喜欢它,如果你传递编码密钥它会抛出异常。
所以为了解决这个问题,我使用 System.Net.WebUtility.UrlDecode(s3Uri.Key)
解码了密钥。所以新的下载方法看起来像
public async Task DownloadFileAsync(string destinationFilePath, string s3Url)
{
var s3Uri = new S3UrlParser(s3Url);
var s3Client = new AmazonS3Client(s3Uri.Region);
GetObjectRequest getObjectRequest = new GetObjectRequest
{
BucketName = s3Uri.Bucket,
Key = System.Net.WebUtility.UrlDecode(s3Uri.Key)
};
// dispose the underline stream when writing to local file system is done
using (var getObjectResponse = await s3Client.GetObjectAsync(getObjectRequest).ConfigureAwait(false))
{
await getObjectResponse.WriteResponseStreamToFileAsync(destinationFilePath, false, default(System.Threading.CancellationToken)).ConfigureAwait(false);
}
}
我在解析名称中带有 space 和正斜杠的文件名时遇到了同样的问题。未转义的正斜杠的问题是大多数工具将每个正斜杠视为 S3 中的目录分隔符。
所以像 myfilename = "file/name.pdf" 这样附加到虚拟路径的简单名称会变成; “directory/subdirectory/myfile/name.pdf”(这里它现在有一个名为 myfile 的目录,它不是预期的)
可以通过在上传时转义文件名“directory/subdirectory/myfile%2fname.pdf”然后按照此 post 的答案中的建议进行解码来缓解这种情况。
在 AWS Explorer 中遇到由额外的双斜杠引起的错误斜杠和空白文件夹名称的错误,例如“//”(此处进一步描述:https://webapps.stackexchange.com/a/120135)
我得出的结论是,最好从任何文件名比例(而不是虚拟目录)中完全去除正斜杠,而不是依赖于上传时转义和下载时取消转义。像其他人一样将正斜杠视为保留字符:
在转义白色space 符号“+”的情况下,取消转义字符通常是安全的,它会按预期工作。
文件名比例可以使用
编码
System.Web.HttpUtility.Encode(input);
non-acceptable 文件名字符列表可以在文件创建时使用 Path.GetInvalidPathChars()
进行比较
文档:https://docs.microsoft.com/en-us/dotnet/api/system.io.path.getinvalidfilenamechars
我正在使用 AWSSDK.S3
版本 3.3.17.2
和 AWSSDK.Core
版本 3.3.21.16
上传一个文件,然后下载同一个文件。下面的代码无法下载文件如果文件名有空格(或#
)
public class AmazonS3
{
public async Task<string> UploadFileAsync(string sourceFile, string s3BucketUrl)
{
AmazonS3Uri s3Uri = new AmazonS3Uri(s3BucketUrl);
using (var s3 = new AmazonS3Client(s3Uri.Region))
{
using (TransferUtility utility = new TransferUtility(s3))
{
TransferUtilityUploadRequest request = new TransferUtilityUploadRequest
{
BucketName = s3Uri.Bucket,
ContentType = "application/pdf",
FilePath = sourceFile,
Key = s3Uri.Key + Path.GetFileName(sourceFile),
};
await utility.UploadAsync(request).ConfigureAwait(false);
}
}
return Path.Combine(s3BucketUrl, Path.GetFileName(sourceFile));
}
public async Task DownloadFileAsync(string destinationFilePath, string s3Url)
{
var s3Uri = new AmazonS3Uri(s3Url);
var s3Client = new AmazonS3Client(s3Uri.Region);
GetObjectRequest getObjectRequest = new GetObjectRequest
{
BucketName = s3Uri.Bucket,
Key = s3Uri.Key
};
// dispose the underline stream when writing to local file system is done
using (var getObjectResponse = await s3Client.GetObjectAsync(getObjectRequest).ConfigureAwait(false))
{
await getObjectResponse.WriteResponseStreamToFileAsync(destinationFilePath, false, default(System.Threading.CancellationToken)).ConfigureAwait(false);
}
}
}
然后出于测试目的,我正在上传文件并再次下载相同的文件
AmazonS3 s3 = new AmazonS3();
var uploadedFileS3Link = await s3.UploadFileAsync("C:\temp\my test file.pdf", @"https://mybucket.s3-us-west-2.amazonaws.com/development/test/");
// get exception at line below
await s3.DownloadFileAsync("C:\temp\downloaded file.pdf",uploadedFileS3Link );
我收到异常
Amazon.S3.AmazonS3Exception: The specified key does not exist. ---> Amazon.Runtime.Internal.HttpErrorResponseException: The remote server returned an error: (404) Not Found. ---> System.Net.WebException: The remote server returned an error: (404) Not Found. at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func
2 endFunction, Action1 endAction, Task
1 promise, Boolean requiresSynchronization) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at ......
为简洁起见删除了剩余的异常
存储桶中确实存在该文件。事实上,我可以复制并粘贴 s3url(即 uploadedFileS3Link
变量的值)并通过浏览器下载文件。
(请注意,实际上我正在尝试下载 1000 多个已上传且名称中包含空格的文件。因此在上传时删除空格不是一种选择)
更新 1 我注意到 S3 浏览器 Url 编码文件名
我尝试使用编码文件路径下载文件 https://mybucket.s3-us-west-2.amazonaws.com/development/test/my%20test%20file.pdf
但还是不行
所以我终于找到了问题所在。我正在使用 AmazonS3Uri class 来解析给定的 S3 url 并获取密钥、存储桶和区域。 AmazonS3Uri
return 是我的密钥 development/test/my%20test%20file.pdf
因为在内部 AmazonS3Uri
正在使用 System.Uri
构建 Uri 然后 returns AbsolutePath
其中 returns 编码路径作为 Key (它应该 return 本地路径作为键吗?)
我不知道为什么 AmazonS3Client
不喜欢它,如果你传递编码密钥它会抛出异常。
所以为了解决这个问题,我使用 System.Net.WebUtility.UrlDecode(s3Uri.Key)
解码了密钥。所以新的下载方法看起来像
public async Task DownloadFileAsync(string destinationFilePath, string s3Url)
{
var s3Uri = new S3UrlParser(s3Url);
var s3Client = new AmazonS3Client(s3Uri.Region);
GetObjectRequest getObjectRequest = new GetObjectRequest
{
BucketName = s3Uri.Bucket,
Key = System.Net.WebUtility.UrlDecode(s3Uri.Key)
};
// dispose the underline stream when writing to local file system is done
using (var getObjectResponse = await s3Client.GetObjectAsync(getObjectRequest).ConfigureAwait(false))
{
await getObjectResponse.WriteResponseStreamToFileAsync(destinationFilePath, false, default(System.Threading.CancellationToken)).ConfigureAwait(false);
}
}
我在解析名称中带有 space 和正斜杠的文件名时遇到了同样的问题。未转义的正斜杠的问题是大多数工具将每个正斜杠视为 S3 中的目录分隔符。
所以像 myfilename = "file/name.pdf" 这样附加到虚拟路径的简单名称会变成; “directory/subdirectory/myfile/name.pdf”(这里它现在有一个名为 myfile 的目录,它不是预期的)
可以通过在上传时转义文件名“directory/subdirectory/myfile%2fname.pdf”然后按照此 post 的答案中的建议进行解码来缓解这种情况。
在 AWS Explorer 中遇到由额外的双斜杠引起的错误斜杠和空白文件夹名称的错误,例如“//”(此处进一步描述:https://webapps.stackexchange.com/a/120135)
我得出的结论是,最好从任何文件名比例(而不是虚拟目录)中完全去除正斜杠,而不是依赖于上传时转义和下载时取消转义。像其他人一样将正斜杠视为保留字符:
在转义白色space 符号“+”的情况下,取消转义字符通常是安全的,它会按预期工作。
文件名比例可以使用
编码System.Web.HttpUtility.Encode(input);
non-acceptable 文件名字符列表可以在文件创建时使用 Path.GetInvalidPathChars()
进行比较文档:https://docs.microsoft.com/en-us/dotnet/api/system.io.path.getinvalidfilenamechars