使用预先签名的网址

时间:2016-06-11 05:38:50

标签: amazon-web-services amazon-s3 xmlhttprequest react-native pre-signed-url

尝试使用预先签名的网址从React Native上传图片到S3。这是我的代码:

在节点中生成预先签名的网址:

const s3 = new aws.S3();

const s3Params = {
  Bucket: bucket,
  Key: fileName,
  Expires: 60,
  ContentType: 'image/jpeg',  
  ACL: 'public-read'
};

return s3.getSignedUrl('putObject', s3Params);

这是对S3的RN请求:

var file = {
  uri: game.pictureToSubmitUri,
  type: 'image/jpeg',
  name: 'image.jpg',
};

const xhr = new XMLHttpRequest();
var body = new FormData();
body.append('file', file);
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
  if(xhr.readyState === 4){
    if(xhr.status === 200){
      alert('Posted!');
    }
    else{
      alert('Could not upload file.');
   }
 }
};
xhr.send(body);

game.pictureToSubmitUri = assets-library://asset/asset.JPG?id=A282A2C5-31C8-489F-9652-7D3BD5A1FAA4&ext=JPG

signedRequest = https://my-bucket.s3-us-west-1.amazonaws.com/8bd2d4b9-3206-4bff-944d-e06f872d8be3?AWSAccessKeyId=AKIAIOLHQY4GAXN26FOQ&Content-Type=image%2Fjpeg&Expires=1465671117&Signature=bkQIp5lgzuYrt2vyl7rqpCXPcps%3D&x-amz-acl=public-read

错误讯息:

<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>

我可以使用生成的url成功卷曲并映像到S3,我似乎能够成功地从RN发送到requestb.in(但是我只能在requestb.in上看到原始数据,所以不是100%确定图像是正确的。)

基于这一切,我已将我的问题缩小到1)我的图像没有正确上传期间,或者2)不知何故S3想要我的请求的方式与它的进入方式不同。

任何帮助都是muuuuuucchhhh赞赏!

更新

如果正文只是文字({&#39;数据&#39;:&#39; foo&#39;}),可以成功地从RN发布到S3。也许AWS不喜欢多种数据?如何只在RN发送文件???

5 个答案:

答案 0 :(得分:12)

FormData会创建multipart/form-data个请求。 S3 PUT对象需要其请求主体为文件。

您只需将您的文件发送到请求正文中,而不将其包含在FormData

function uploadFile(file, signedRequest, url) {
  const xhr = new XMLHttpRequest();
  xhr.open('PUT', signedRequest);
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if(xhr.status === 200) {
        alert(url);
      } else {
        alert('Could not upload file.');
      }
    }
  };
  xhr.send(file);
};

例如,在浏览器中查看https://devcenter.heroku.com/articles/s3-upload-node。另请确保您的Content-Type标题与签名的网址请求相匹配。

答案 1 :(得分:3)

在iOS和Android上上传到预先签名的S3 URL上,我浪费了太多时间。 对我有用的是rn-fetch-blob lib

代码段:

create or replace sequence seq;
create or replace function cyclic_seq() returns int as 'mod(seq.nextval, 3)';
create or replace table x(s string, i int default cyclic_seq());

insert into x(s) values('a');
insert into x(s) values('b');
insert into x(s) values('c');
insert into x(s) values('d');
insert into x(s) values('e');
insert into x(s) values('f');

select * from x;
---+---+
 S | I |
---+---+
 a | 1 |
 b | 2 |
 c | 0 |
 d | 1 |
 e | 2 |
 f | 0 |
---+---+

答案 2 :(得分:1)

"rn-fetch-blob": 0.12.0,
"react-native": 0.61.5

此代码适用于Android和iOS

const response = await RNFetchBlob.fetch(
  'PUT',
  presignedUrl,
  {
    'Content-Type': undefined
  },
  RNFetchBlob.wrap(file.path.replace('file://', '')),
)

注意{'Content-Type': undefined}对于iOS是必需的

答案 3 :(得分:0)

对不起,如果没有任何机构可以使用。我花了5天的时间才能使它工作。 5个疯狂的日子,没有结果,直到我小睡一会后昏昏欲睡的眼睛变成绿色。猜猜我做了一个甜蜜的梦,带来了这个主意。这么快地说,您在服务器上有一个端点,以从react本机端或从React端或任何Web边界生成请求的签名URL。我将对react native和react(可以用于html页面和有角度的页面)都这样做。

网络方法

将图像上传到S3桶预签名的URI

/*
      Function to carry out the actual PUT request to S3 using the signed request from the app.
    */
    function uploadFile(file, signedRequest, url){
     // document.getElementById('preview').src = url; // THE PREVIEW PORTION
        //    document.getElementById('avatar-url').value = url; //
      const xhr = new XMLHttpRequest();
      xhr.open('PUT', signedRequest);
      xhr.onreadystatechange = () => {
        if(xhr.readyState === 4){
          if(xhr.status === 200){
            document.getElementById('preview').src = url;
           // document.getElementById('avatar-url').value = url;
          }
          else{
            alert('Could not upload file.');
          }
        }
      };
      xhr.send(file);
    }

    /*
      Function to get the temporary signed request from the app.
      If request successful, continue to upload the file using this signed
      request.
    */
    function getSignedRequest(file){
      const xhr = new XMLHttpRequest();

      xhr.open('GET', 'http://localhost:1234'+`/sign-s3?file-name=${file.name}&file-type=${file.type}`);
        xhr.setRequestHeader('Access-Control-Allow-Headers', '*');
    xhr.setRequestHeader('Content-type', 'application/json');
    xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
      xhr.onreadystatechange = () => {
        if(xhr.readyState === 4){
          if(xhr.status === 200){
            const response = JSON.parse(xhr.responseText);
            uploadFile(file, response.signedRequest, response.url);
          }
          else{
            alert('Could not get signed URL.');
          }
        }
      };
      xhr.send();
    }

    /*
     Function called when file input updated. If there is a file selected, then
     start upload procedure by asking for a signed request from the app.
    */
    function initUpload(){
      const files = document.getElementById('file-input').files;
      const file = files[0];
      if(file == null){
        return alert('No file selected.');
      }
      getSignedRequest(file);
    }

    /*
     Bind listeners when the page loads.
    */


   //check if user is actually on the profile page
//just ensure that the id profile page exist  on your html
  if (document.getElementById('profile-page')) {
    document.addEventListener('DOMContentLoaded',() => {

      ///here is ur upload trigger bttn effect

        document.getElementById('file-input').onchange = initUpload;
    });

  }



答案 4 :(得分:0)

为了反应迅速,我将不会使用任何3RD派对盖。

我有我的选择图像功能,可以选择图像并使用xhr上传

const pickImage = async () => {
    let result = await ImagePicker.launchImageLibraryAsync({
     // mediaTypes: ImagePicker.MediaTypeOptions.All,
      allowsEditing: true,
      aspect: [4, 3],
      quality: 1,
      base64:true
    });

    console.log(result);






    if (!result.cancelled) {
     // setImage(result.uri);
      let base64Img = `data:image/jpg;base64,${result.uri}`;




       // ImagePicker saves the taken photo to disk and returns a local URI to it
  let localUri = result.uri;
  let filename = localUri.split('/').pop();

  // Infer the type of the image
  let match = /\.(\w+)$/.exec(filename);
  let type = match ? `image/${match[1]}` : `image`;

  // Upload the image using the fetch and FormData APIs
  let formData = new FormData();
  // Assume "photo" is the name of the form field the server expects
  formData.append('file', { uri: base64Img, name: filename, type });

  const xhr = new XMLHttpRequest();


  xhr.open('GET', ENVIRONMENTS.CLIENT_API+`/sign-s3?file-name=${filename}&file-type=${type}`);
  xhr.setRequestHeader('Access-Control-Allow-Headers', '*');
xhr.setRequestHeader('Content-type', 'application/json');
// xhr.setRequestHeader('Content-type', 'multipart/form-data');
xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
 xhr.setRequestHeader('X-Amz-ACL', 'public-read') //added
xhr.setRequestHeader('Content-Type', type) //added
xhr.onreadystatechange = () => {
  if(xhr.readyState === 4){
    if(xhr.status === 200){
      const response = JSON.parse(xhr.responseText);
      alert(JSON.stringify( response.signedRequest, response.url))
      // uploadFile(file, response.signedRequest, response.url);
      // this.setState({imagename:file.name})
      const xhr2 = new XMLHttpRequest();

            xhr2.open('PUT', response.signedRequest);
            xhr2.setRequestHeader('Access-Control-Allow-Headers', '*');
            xhr2.setRequestHeader('Content-type', 'application/json');
            // xhr2.setRequestHeader('Content-type', 'multipart/form-data');
            xhr2.setRequestHeader('Access-Control-Allow-Origin', '*');
            //  xhr2.setRequestHeader('X-Amz-ACL', 'public-read') //added
            xhr2.setRequestHeader('Content-Type', type) //added
            xhr2.onreadystatechange = () => {
              if(xhr2.readyState === 4){
                if(xhr2.status === 200){

                  alert("successful upload ")
                }
                else{
                  // alert('Could not upload file.');
                  var error = new Error(xhr.responseText)
                  error.code = xhr.status;
                  for (var key in response) error[key] = response[key]
                  alert(error)
                }
              }
            };
            xhr2.send( result.base64)
    }
    else{
      alert('Could not get signed URL.');
    }
  }
};
xhr.send();








    }


  };






then some where in the render method

<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Button title="Pick an image from camera roll" onPress={pickImage} />
      {image && <Image source={{ uri: image }} style={{ width: 200, height: 200 }} />}
    </View>


hope it helps any one who doesnt want sleepless nights like me.