React Native上传图片到Amazon S3

8
我希望从我的应用程序上传图像到S3服务器。我已经计算了所有的数据和代码(在电脑上使用curl进行测试),但是我无法正确调用'fetch'。我得到的响应是:
“至少有一个您指定的前提条件不成立。条件:Bucket POST必须是enclosure-type-multipart/form-data”
如何在react-native的fetch中重新创建表单数据?没有FormData可以添加,然后像fetches示例中那样发送它。
编辑: 谢谢@philipp-von-weitershausen,很高兴你添加了这个功能。但是我在调用它时遇到了一些麻烦。我得到了“Unsupported BodyInit type”的错误。发现是因为在fetch.js中,“support.formData”返回false。当我调用fetch时,我错过了什么?
我的代码示例:
 var form = new FormData();
 form.append("FormData", true)
 form.append("name", this.state.invoiceNumber)
 form.append("key", this.state.invoiceNumber)
 form.append("Content-Type", "image/png")
 form.append('file', this.props.uri)
 //alert(FormData.prototype.isPrototypeOf(form))

  fetch(amazon_url,{body: form,mode: "FormData", method: "post", headers: {"Content-Type": "multipart/FormData"}})
          .then((response) => response.json())
          .catch((error) => {
             alert("ERROR " + error)
          })
          .then((responseData) => {
             alert("Succes "+ responseData)
          })
          .done();

你有这个问题的最终工作代码示例吗?我也遇到了困难。 - Moss Palmer
实际上,你有没有一个例子,只能发布表单变量(不包括图像 - 因为下一个RN构建将添加图像支持)? - Moss Palmer
react-native-fetch-blob 可以为您完成此操作。 - Xeijp
4个回答

9

@Michał Zubrzycki 谢谢你,你的上传图片代码对我有帮助,只需稍作修改即可。

const photo = {
  uri: user.profilePicture,
  type: "image/jpeg",
  name: "photo.jpg"
};
const form = new FormData();
form.append("ProfilePicture", photo);
fetch(Constants.API_USER + "me/profilePicture", {
  body: form,
  method: "PUT",
  headers: {
    "Content-Type": "multipart/form-data",
    Authorization: "Bearer " + user.token
  }
})
  .then(response => response.json())
  .catch(error => {
    console.log("ERROR ", error);
  })
  .then(responseData => {
    console.log("Success", responseData);
  })
  .done();

6

有些人询问,所以我来发布一下我是如何做到的。这是很久以前完成的,如果你有任何评论或者发现有什么做得不好的地方,欢迎批评;)

从相机胶卷中读取照片并存储在“latestPhoto”中。

将照片上传到S3服务器:

步骤1:生成数据:

_addTextParam() {
    var textParams = this.state.textParams;
    s3_upload_id = this.makeid()
    textParams.push({ name: "key", value: this.state.upload_path + s3_upload_id + '/' + this.state.att_name + ".jpg" })
    textParams.push({ name: "AWSAccessKeyId", value: this.state.key })
    textParams.push({ name: "acl", value: "public-read" })
    textParams.push({ name: "success_action_status", value: "201" })
    textParams.push({ name: "policy", value: this.state.policy })
    textParams.push({ name: "signature", value: this.state.signature })
    textParams.push({ name: "Content-Type", value: "image/jpeg" })

    this.setState({ textParams: textParams });
  }

第二步:发送数据。
  _send() {

    this._addTextParam()
    var xhr = new XMLHttpRequest();
    xhr.open('POST', "http://" + this.state.fs_domain + "." + this.state.server);
    xhr.onload = () => {
      this.setState({ isUploading: false });
      if (xhr.status !== 201) {
        AlertIOS.alert( 
          'Upload failed',
          'Expected HTTP 200 OK response, got ' + xhr.status + "/" + xhr.responseText
        );
        return;
      }

      if (!xhr.responseText) {
        AlertIOS.alert(
          'Upload failed',
          'No response payload.'
        );
        return;
      }
      var index = xhr.responseText.indexOf( "http://" + this.state.fs_domain + "." + this.state.server);
      if (index === -1) {
        AlertIOS.alert(
          'Upload failed',
          'Invalid response payload.'
        );
        return;
      }
      var url = xhr.responseText.slice(index).split('\n')[0];
      this.state.s3_file_id = xhr.responseText.split('Tag>"')[1].split('"')[0]
      this.state.s3_file_path = xhr.responseText.split('Location>')[1].split('<')[0]
      this.setState({ isUploading: false });
      RCTDeviceEventEmitter.emit('Uploaded')

    };
    var formdata = new FormData();

    this.state.textParams.forEach((param) => {
        formdata.append(param.name, param.value)
      }
    );

    formdata.append('file', {...this.state.latestPhoto, name: (this.state.att_name+".jpg") });

    xhr.send(formdata);
    this.setState({ isUploading: true });

  },

你是如何生成签名的?我在网上找到的所有模块都依赖于一些节点特性,比如 httpfs - Harry Moreno
@HarryMoreno 照片是通过 CameraRoll 加载的,而签名则是从我们使用 Ruby on Rails 编写的主应用程序中获取的。但我认为您可以根据 AWS 文档重新创建它。 - Michał Zubrzycki

5

支持通过XHR FormData API实现混合载荷(JS字符串+图像载荷)的React Native multipart/form-data即将推出。它应该很快会在GitHub上发布。


2
aaaand它落地了:https://github.com/facebook/react-native/commit/f4bf80f3ea3b7ed6aee8b068ec1a289e0965eb5e - Philipp von Weitershausen
太好了!我看到你也改变了一些调用fetch的方法?它停止工作或显示错误:未定义的函数(评估RCTDataMenager.queryData('http', {method:method,url:url,data:data,headers:headers})。有没有什么快速提示,发生了什么变化? - Michał Zubrzycki
没事了,我这个文件夹里有两个不同版本的React Native,它们之间不兼容。 - Michał Zubrzycki

3

S3选项:

// this.state.s3options in YourComponent
{
  "url": "https://yourapp.s3.eu-central-1.amazonaws.com",
  "fields": {
    "key": "cache/22d65141b48c5c44eaf93a0f6b0abc30.jpeg",
    "policy": "eyJleHBpcm...1VDE0Mzc1OVoifV19",
    "x-amz-credential": "AK...25/eu-central-1/s3/aws4_request",
    "x-amz-algorithm": "AWS4-HMAC-SHA256",
    "x-amz-date": "20161125T143759Z",
    "x-amz-signature": "87863c360...b9b304bfe650"
  }
}

组件:

class YourComponent extends Component {
  // ...

  // fileSource looks like: {uri: "content://media/external/images/media/13", isStatic: true}
  async uploadFileToS3(fileSource) {
    try {
      var formData = new FormData();
      // Prepare the formData by the S3 options
      Object.keys(this.state.s3options.fields).forEach((key) => {
        formData.append(key, this.state.s3options.fields[key]);
      });
      formData.append('file', {
        uri: fileSource.uri,
        type: 'image/jpeg',
      });
      formData.append('Content-Type', 'image/jpeg')

      var request = new XMLHttpRequest();
      request.onload = function(e) {
        if (e.target.status === 204) {
          // Result in e.target.responseHeaders.Location
          this.setState({avatarSourceRemote: {uri: e.target.responseHeaders.Location}})
        }
      }.bind(this)
      request.open('POST', this.state.s3options.url, true);
      request.setRequestHeader('Content-type', 'multipart/form-data');
      request.send(formData);
    } catch(error) {
      console.error(error);
    }
  }

  // Example display the uploaded image
  render() {
    if (this.state.avatarSourceRemote) {
      return (
        <Image source={this.state.avatarSourceRemote} style={{width: 100, height: 100}} />
      );
    } else {
      return (
        <Text>No Image</Text>
      );
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接