14

一直在尝试使用预签名的 url 从 React Native 将图像上传到 S3,但没有成功。这是我的代码:

在节点中生成预签名的 url:

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

签名请求 =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 希望我的请求的方式与它的传入方式不同。

任何帮助将不胜感激!

更新

如果正文只是文本({'data': 'foo'}),则可以成功从 RN 发布到 S3。也许 AWS 不喜欢多种形式的数据?我怎样才能在 RN 中作为文件发送???

4

5 回答 5

17

FormData将创建一个multipart/form-data请求。S3PUT对象需要它的请求主体是一个文件。

您只需要在请求正文中发送文件而不将其包装成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标头与签名的 URL 请求相匹配。

于 2016-06-29T14:28:02.100 回答
17

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

代码片段:

import RNFetchBlob from 'rn-fetch-blob'

const preSignedURL = 'pre-signed url'
const pathToImage = '/path/to/image.jpg' // without file:// scheme at the beginning
const headers = {}

RNFetchBlob.fetch('PUT', preSignedURL, headers, RNFetchBlob.wrap(pathToImage))
于 2018-08-10T21:44:05.920 回答
4
"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 需要备注

于 2020-11-03T14:28:19.147 回答
0

抱歉,如果没有一个适用于任何机构。我花了 5 天时间才让这个工作。5天没有结果,直到我睡眼惺忪的小睡后变成绿色。猜猜我做了一个甜蜜的梦,带来了这个想法。这么快说你在你的服务器上有一个端点,可以为来自反应本地端或反应端或任何网络前沿的请求生成签名 URL。我会为 react native 和 react 做这个(可以为 html 页面和 angular 页面服务)。

网络方法

将图像上传到 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;
    });

  }



于 2020-06-03T03:31:49.813 回答
0

对于 React Native,我不会使用任何 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.
于 2020-06-03T03:38:16.570 回答