Javascript 如何使用预先签名的URL而不是凭据直接从浏览器上载到AWS S3?

Javascript 如何使用预先签名的URL而不是凭据直接从浏览器上载到AWS S3?,javascript,amazon-web-services,amazon-s3,Javascript,Amazon Web Services,Amazon S3,我们希望使用Javascript AWS SDK将文件上载到S3,但根本不使用凭据。 使用凭据上传是可行的,但我们无法为每个应用程序用户生成AWS IAM用户(或者我们应该吗?) 因此,与使用GET类似,我们希望服务器生成一个预签名的URL,将其发送到浏览器,并将浏览器上载到该URL 然而,没有关于如何做到这一点的例子。 此外,如果没有设置凭据,即使在发出上传到S3请求之前,SDK也会出现以下错误: code: "CredentialsError" message: "No credential

我们希望使用Javascript AWS SDK将文件上载到S3,但根本不使用凭据。 使用凭据上传是可行的,但我们无法为每个应用程序用户生成AWS IAM用户(或者我们应该吗?)

因此,与使用GET类似,我们希望服务器生成一个预签名的URL,将其发送到浏览器,并将浏览器上载到该URL

然而,没有关于如何做到这一点的例子。 此外,如果没有设置凭据,即使在发出上传到S3请求之前,SDK也会出现以下错误:

code: "CredentialsError"
message: "No credentials to load"
JS SDK文档提到了这一点,因此似乎有可能:

Pre-signing a putObject (asynchronously)
var params = {Bucket: 'bucket', Key: 'key'};
    s3.getSignedUrl('putObject', params, function (err, url) {
      console.log('The URL is', url);
});

我可以提出两种方法:

1-您可以在应用程序中生成预签名表单,只需一个凭据

见文件:

2-您可以使用web identity federation并使用google、facebook或amazon登录:

见文件:


游乐场:

我更喜欢通过github采用这种更干净的方法:

如果已经为浏览器生成了一个预先签名的URL,那么只需发送一个带有该URL和负载的XHR请求,然后将其上载到S3。SDK不需要这样做。下面是一个jQuery示例:

$.ajax({
  url: presignedUrl, // the presigned URL
  type: 'PUT',
  data: 'data to upload into URL',
  success: function() { console.log('Uploaded data successfully.'); }
});

在project中,根据我现在正在做的工作,我将文件从客户端直接上传到S3,在我的例子中,它只需几个步骤即可工作:

  • 从服务器请求带有上载设置的预签名表单(它是在服务器上签名的,因为我无法将访问密钥传递给客户端,而且我需要对上载应用一些限制)
  • 使用XHR2将文件上载到S3(对于旧浏览器,您可以使用隐藏iframe的hack或flash等浏览器插件)

  • 它有主要的代码部分:

    让老问题安静下来,但它确实帮助我最终完成了它。 我的解决方案基于PHP和JavaScript以及jQuery

    我已将整个解决方案很好地包装在中,但以下是要点:

    api.php:

    <?php
    require_once '/server/path/to/aws-autoloader.php';
    use Aws\Common\Aws;
    
    $BUCKET = "my-bucket";
    $CONFIG = "path-to-iam-credentials-file-relative-to-root.php"
    
    function getSignedUrl($filename, $mime) {
        $S3 = Aws::factory( $CONFIG )->get('S3');
        if(!$filename) {
            return $this->error('filename missing');
        }
        if(!$mime) {
            return $this->error('mime-type missing');
        }
        $final_filename = $this->get_file_name($filename);
        try {
            $signedUrl = $S3->getCommand('PutObject', array(
                'Bucket' => $BUCKET,
                'Key' => $this->folder . $final_filename,
                'ContentType' => $mime,
                'Body'        => '',
                'ContentMD5'  => false
            ))->createPresignedUrl('+30 minutes');
        } catch (S3Exception $e) {
            echo $e->getMessage() . "\n";
        }
        $signedUrl .= '&Content-Type='.urlencode($mime);
        return $signedUrl;
    }
    
    
    echo getSignedUrl($_GET['filename'],$_GET['mimetype']);
    
    
    *
    得到
    邮递
    放
    头
    删除
    3000
    *
    
    如果您不使用jQuery,这是前端所需的最低限度:

    var xhr = new XMLHttpRequest();
    xhr.open('PUT', signedUrl, true);
    xhr.setRequestHeader('Content-Type', signedUrlContentType);
    xhr.onload = () => {
      if (xhr.status === 200) {
        // success!
      }
    };
    xhr.onerror = () => {
      // error...
    };
    xhr.send(file); // `file` is a File object here 
    
    请参阅文件对象文档:

    然后,您可以像往常一样添加上载进度:

    xhr.upload.onprogress = (event) => {
      if (event.lengthComputable) {
        var percent = Math.round((event.loaded / event.total) * 100)
        console.log(percent);
      }
    };
    

    请添加
    ACL
    ContentType
    ,它将使其正常工作

    const param = {
          Bucket: 'Bucket',
          Key: 'fiileName',
          ACL: 'public-read',
          ContentType: 'fileType'
        };
    s3.getSignedUrl('putObject', param, function (err, url) {
             console.log('The URL is', url);
        });
    
    生成Url

    const AWS = require("aws-sdk");
    const s3 = new AWS.S3({
       endpoint: 's3-ap-south-1.amazonaws.com',   // Put you region
       accessKeyId: 'AKXXXXXXXXXXXXXXXA6U',       // Put you accessKeyId
       secretAccessKey: 'kzFHoXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXssoGp',   // Put you accessKeyId
       Bucket: 'Bucket-Name',         // Put your bucket name
       signatureVersion: 'v4',
       region: 'ap-south-1'           // Put you region
    });
    
    const getSingedUrlforPut = async () => {
    const params = {
        Bucket: 'Bucket-Name',
        Key: '317ec11af14a46b89f400bcf8f9fff1222.pdf',
        Expires: 60 * 5
      };
    try {
        const url = await new Promise((resolve, reject) => {
          s3.getSignedUrl('putObject', params, (err, url) => {
            err ? reject(err) : resolve(url);
          });
        });
        console.log(url)
      } catch (err) {
        if (err) {
          console.log(err)
        }
      }
    }
    getSingedUrlforPut()
    
    通过ajax上传文件

    var form = new FormData();
    form.append("", fileInput.files[0], "director_pan_af8ef2d261c46877f95038622c96e7c0.pdf");
    var settings = {
      "url": "https://sme-testing.s3-ap-south-1.amazonaws.com/317ec11af14a46b89f400bcf8f9fff1222.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXXXXXXXXXX6U%2F20200525%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20200525T083419Z&X-Amz-Expires=300&X-Amz-Signature=ea063731d7d043b62d0dc7c0984f4d5792c7f7f41e9ffb52a97d62adadcef422&X-Amz-SignedHeaders=host",
      "method": "PUT",
      "timeout": 0,
      "processData": false,
      "mimeType": "multipart/form-data",
      "contentType": false,
      "data": form
    };
    $.ajax(settings).done(function (response) {
      console.log(response);
    });
    

    你能再解释一下吗。我的意思是,如果没有AWS S3 SDK,你将如何上传?您需要验证凭据r8?不,您不必验证凭据,因为您在服务器上使用了预签名url(知道您的密钥和秘密)并将此签名url发送到客户端。然后,客户端使用此预签名来上载文件。当我尝试此操作时,我不断得到
    [Exception…”对受限URI的访问被拒绝“code:”1012“nsresult:“0x805303f4(NS\u ERROR\u DOM\u BAD\u URI)”位置:“
    我假设它与某些CORS策略有关,但目前不知道如何修复它。您是否遇到了此问题?(使用firefox 31.4.0)这里的密钥是什么?此外,如果没有访问ID密钥和机密密钥,您如何向服务器请求已签名的URL?欢迎使用SO。通常会在回答中显示所需代码的最相关部分,以便将搜索保存在web上。尝试了许多不同的方法后,第一次成功。感谢ScottWhat进入get\u file\u name()?
    const param = {
          Bucket: 'Bucket',
          Key: 'fiileName',
          ACL: 'public-read',
          ContentType: 'fileType'
        };
    s3.getSignedUrl('putObject', param, function (err, url) {
             console.log('The URL is', url);
        });
    
    const AWS = require("aws-sdk");
    const s3 = new AWS.S3({
       endpoint: 's3-ap-south-1.amazonaws.com',   // Put you region
       accessKeyId: 'AKXXXXXXXXXXXXXXXA6U',       // Put you accessKeyId
       secretAccessKey: 'kzFHoXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXssoGp',   // Put you accessKeyId
       Bucket: 'Bucket-Name',         // Put your bucket name
       signatureVersion: 'v4',
       region: 'ap-south-1'           // Put you region
    });
    
    const getSingedUrlforPut = async () => {
    const params = {
        Bucket: 'Bucket-Name',
        Key: '317ec11af14a46b89f400bcf8f9fff1222.pdf',
        Expires: 60 * 5
      };
    try {
        const url = await new Promise((resolve, reject) => {
          s3.getSignedUrl('putObject', params, (err, url) => {
            err ? reject(err) : resolve(url);
          });
        });
        console.log(url)
      } catch (err) {
        if (err) {
          console.log(err)
        }
      }
    }
    getSingedUrlforPut()
    
    var form = new FormData();
    form.append("", fileInput.files[0], "director_pan_af8ef2d261c46877f95038622c96e7c0.pdf");
    var settings = {
      "url": "https://sme-testing.s3-ap-south-1.amazonaws.com/317ec11af14a46b89f400bcf8f9fff1222.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIXXXXXXXXXXXX6U%2F20200525%2Fap-south-1%2Fs3%2Faws4_request&X-Amz-Date=20200525T083419Z&X-Amz-Expires=300&X-Amz-Signature=ea063731d7d043b62d0dc7c0984f4d5792c7f7f41e9ffb52a97d62adadcef422&X-Amz-SignedHeaders=host",
      "method": "PUT",
      "timeout": 0,
      "processData": false,
      "mimeType": "multipart/form-data",
      "contentType": false,
      "data": form
    };
    $.ajax(settings).done(function (response) {
      console.log(response);
    });