当前位置: 首页 > 面试题库 >

客户端如何将可见文件上传到Amazon S3?

法弘壮
2023-03-14
问题内容

首先,我通常不愿发布这个问题,因为我一直觉得互联网上的所有内容都有答案。在花了无数小时寻找这个问题的答案之后,我终于放弃了这个声明。

这有效:

s3.getSignedUrl('putObject', params);

我想做什么?

  1. 使用getSignedUrl方法通过PUT(从客户端)将文件上传到Amazon S3
  2. 允许任何人查看上载到S3的文件

注意: 如果有一种更简便的方法允许客户端(iPhone)使用预签名的URL(并且不暴露客户端的凭据)将其上传到Amazon S3,我将不知所措。

主要问题*

  1. 查看AWS管理控制台时,上传的文件具有空白的权限和元数据集。
  2. 在查看上传的文件时(即通过在AWS管理控制台中双击文件),我收到一个AccessDenied错误。

我尝试了什么?

尝试#1:我的原始代码

在NodeJS中,我生成一个预签名的URL,如下所示:

var params = {Bucket: mybucket, Key: "test.jpg", Expires: 600};
s3.getSignedUrl('putObject', params, function (err, url){
  console.log(url); // this is the pre-signed URL
});

预签名的URL如下所示:

https://mybucket.s3.amazonaws.com/test.jpg?AWSAccessKeyId=AABFBIAWAEAUKAYGAFAA&Expires=1391069292&Signature=u%2BrqUtt3t6BfKHAlbXcZcTJIOWQ%3D

现在我通过PUT上传文件

curl -v -T myimage.jpg https://mybucket.s3.amazonaws.com/test.jpg?AWSAccessKeyId=AABFBIAWAEAUKAYGAFAA&Expires=1391069292&Signature=u%2BrqUtt3t6BfKHAlbXcZcTJIOWQ%3D

问题*
我得到上面列出的
主要问题**

尝试#2:在PUT上添加Content-Type和ACL

我还尝试通过替换如下参数来在代码中添加Content-Type和x-amz-acl:

var params = {Bucket: mybucket, Key: "test.jpg", Expires: 600, ACL: "public-read-write", ContentType: "image/jpeg"};

然后我尝试一个很好的PUT:

curl -v -H "image/jpeg" -T myimage.jpg https://mybucket.s3.amazonaws.com/test.jpg?AWSAccessKeyId=AABFBIAWAEAUKAYGAFAA&Content-Type=image%2Fjpeg&Expires=1391068501&Signature=0yF%2BmzDhyU3g2hr%2BfIcVSnE22rY%3D&x-amz-acl=public-read-write

问题
我的终端输出一些错误:

-bash: Content-Type=image%2Fjpeg: command not found
-bash: x-amz-acl=public-read-write: command not found

我也得到上面列出的 *主要问题

尝试#3:将存储桶权限修改为公开

下面列出的所有项目都在AWS管理控制台中打勾了)

Grantee: Everyone can [List, Upload/Delete, View Permissions, Edit Permissions]
Grantee: Authenticated Users can [List, Upload/Delete, View Permissions, Edit Permissions]

铲斗政策

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "Stmt1390381397000",
        "Effect": "Allow",
        "Principal": {
            "AWS": "*"
        },
        "Action": "s3:*",
        "Resource": "arn:aws:s3:::mybucket/*"
    }
]
}

尝试#4:设置IAM权限

我将用户策略设置为:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

AuthenticatedUsers组策略为:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmt1391063032000",
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

尝试#5:设置CORS策略

我为此设置了CORS政策:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

而且…现在我在这里。


问题答案:

更新资料

我有个
消息。根据http://aws.amazon.com/releasenotes/1473534964062833上SDK
2.1.6的发行说明:

"The SDK will now throw an error if ContentLength is passed into an 
Amazon S3 presigned URL (AWS.S3.getSignedUrl()). Passing a 
ContentLength is not supported by the SDK, since it is not enforced on 
S3's side given the way the SDK is currently generating these URLs. 
See GitHub issue #457."

我发现有些情况下必须包含ContentLength(特别是如果您的客户端通过了ContentLength,以便签名会匹配),然后在其他情况下,如果您包含带有参数错误的ContentLength,则getSignedUrl会抱怨:“预签名网址中不支持contentlength
”。我注意到当我更改进行呼叫的机器时,行为将会改变。大概是另一台机器连接到服务器场中的另一台Amazon服务器。

我只能猜测为什么在某些情况下会出现这种行为,而在另一些情况下则不会。也许不是所有的Amazon服务器都已完全升级?无论哪种情况,为处理此问题,我现在都尝试使用ContentLength,如果它给我参数错误,则在没有它的情况下再次调用getSignedUrl。这是解决此SDK奇怪行为的方法。

一个小例子…看起来不是很漂亮,但是您可以理解:

MediaBucketManager.getPutSignedUrl = function ( params, next ) {
    var _self = this;
    _self._s3.getSignedUrl('putObject', params, function ( error, data ) {
        if (error) {
            console.log("An error occurred retrieving a signed url for putObject", error);
            // TODO: build contextual error
            if (error.code == "UnexpectedParameter" && error.message.search("ContentLength") > -1) {
                if (params.ContentLength) delete params.ContentLength
                MediaBucketManager.getPutSignedUrl(bucket, key, expires, params, function ( error, data ) {
                    if (error) {
                        console.log("An error occurred retrieving a signed url for putObject", error);
                    } else {
                        console.log("Retrieved a signed url for putObject:", data);
                        return next(null, data)
                    }
                }); 
            } else {
                return next(error); 
            }
        } else {
            console.log("Retrieved a signed url for putObject:", data);
            return next(null, data);
        }
    });
};

因此,以下内容并不完全正确(在某些情况下会是正确的,但在其他情况下会给您参数错误),但可能会帮助您入门。

旧答案

似乎(对于将签名文件PUT到S3的文件,其中只有公开读取的ACL),当向P3提出请求时,将比较一些头。将它们与传递给getSignedUrl的内容进行比较:

CacheControl: 'STRING_VALUE',
ContentDisposition: 'STRING_VALUE',
ContentEncoding: 'STRING_VALUE',
ContentLanguage: 'STRING_VALUE',
ContentLength: 0,
ContentMD5: 'STRING_VALUE',
ContentType: 'STRING_VALUE',
Expires: new Date || 'Wed De...'

请在此处查看完整列表:http :
//docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-
property

调用getSignedUrl时,您将传递一个“参数”对象(在文档中已经很清楚了),该对象包含Bucket,Key和Expires数据。这是一个(NodeJS)示例:

var params = { Bucket:bucket, Key:key, Expires:expires };
s3.getSignedUrl('putObject', params, function ( error, data ) {
    if (error) {
        // handle error
    } else {
        // handle data
    }
});

不太清楚的是将ACL设置为“公开读取”:

var params = { Bucket:bucket, Key:key, Expires:expires, ACL:'public-read' };

非常模糊的是传递标头的概念,您希望客户端使用带签名的url将其与PUT操作一起传递给S3:

var params = {
    Bucket:bucket,
    Key:key,
    Expires:expires,
    ACL:'public-read',
    ContentType:'image/png',
    ContentLength:7469
};

在上面的示例中,我包含了ContentType和ContentLength,因为在javascript中使用XmlHTTPRequest时包括了这两个标头,并且在Content-
Length的情况下无法更改。我怀疑对于HTTP请求的其他实现(例如Curl等)会是这种情况,因为在提交包含(数据)主体的HTTP请求时,它们是必需的标头。

如果客户端在请求一个signedUrl时不包括有关文件的ContentType和ContentLength数据,则在将文件放置到S3(带有那个signedUrl)时,S3服务将找到客户端请求中包含的标头(因为它们是必需的标头),但签名将不包含它们-
因此,它们将不匹配,操作将失败。

因此,在进行getSignedUrl调用之前,您似乎必须知道要PUT到S3的文件的内容类型和内容长度。这对我来说不是问题,因为我公开了REST端点,以允许我们的客户端在对S3进行PUT操作之前就可以请求签名的url。由于客户端可以访问要提交的文件(此时他们已经准备好提交),因此客户端访问文件大小和类型并从我的端点请求带有该数据的签名URL是一项微不足道的操作。



 类似资料:
  • 问题内容: 我想将一些文件上传到HTTP服务器。基本上,我需要的是对服务器的某种POST请求,其中包含一些参数和文件。我看到了仅上传文件的示例,但没有找到如何也传递其他参数的示例。 什么是最简单,免费的解决方案?有人有我可以学习的文件上传示例吗?我已经搜寻了几个小时,但是(也许只是那几天)找不到我真正需要的东西。最好的解决方案是不涉及任何第三方类或库的东西。 问题答案: 通常,你会用来触发HTTP

  • 我肯定在这里缺少某种序列化程序 任何建议都将不胜感激! 谢谢

  • 当我试图从客户端上传时,我一直得到403。这是因为桶上没有条件吗?如果我只是指定密钥-没有访问密钥,签名或策略-它将上传罚款。 桶策略: CORS(因当地开发而开放) 签名生成 客户:

  • 以下是AWS Amplify GraphQL客户端的官方文档:https://aws-amplify.github.io/amplify-js/media/api_guide.html.本节提供了基本输入的示例。 对于AWS Mobile Appsync SDK For Javascript,这里有一个详细的文档:https://docs.aws.amazon.com/appsync/latest

  • 请在下面找到球衣客户代码以上传多部分文件: 我需要添加字符串fileContentLength和thumbnailContentLength作为标题内容长度。我如何将标题添加为multipart的一部分并提交请求?任何帮助都将不胜感激

  • 问题内容: 您好,我一直在尝试将文件从node.js发送到客户端。 我的代码有效,但是当客户端转到指定的url()时,它将流式传输文件。 从Google Chrome浏览器访问该文件可使文件(.mp3)在播放器中播放。 我的目标是让客户的浏览器下载文件,然后问客户他想在哪里存储文件,而不是在网站上流式传输。 问题答案: 您需要设置一些标题标志。 用下载代替流媒体;