V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
niuer
V2EX  ›  推广

用纯 Javascript 实现 React Native 的文件上传

  •  
  •   niuer · 2016-04-28 11:40:28 +08:00 · 9418 次点击
    这是一个创建于 3133 天前的主题,其中的信息可能已经有所发展或是发生改变。

    React Native 是最近两年最值得花时间跟进的移动开发技术,这个项目始于 2013 年 Facebook 内部的一个黑客马拉松项目,在 2014 年 7 月之前这个项目都偏向于实验性质,直到广告管理团队想要构建一个独立的 iOS 应用,然而这个团队并没有 iOS 开发经验的工程师,于是在接下来的几个月里广告团队和 React Naitve 团队紧密合作,共同推进了该项目的发展。 2015 年 2 月, iOS 版本的 Ads Manager 在 App Store 发布标志着这个产品可以应用在生产环境中,接着 2015 年 3 月,在 Facebook 的 F8 大会上这一项目宣布开源并公开了 ReactNaitve 的源码,与此同时, Android 版本的 React Native 和 Ads Manager 也在开发过程中。 2015 年 6 月, Android 版本的 Ads Manager 发布到 Play Store , 9 月 14 日在千呼万唤的期待声中, Android 版本的 React Native 发布,今年刚刚结束的 F8 大会,微软也拥抱 React Native ,发布他们移植 React Naitve 到 Windows 的进度和计划,并发布了基于 React Naitve 的 F8 UMP 。

    本文将介绍:

    • 如何使用原生 Javascript 上传文件
    • 如何使用七牛云 SDK 上传文件到七牛云 在 App 中文件上传是一个非常重要的需求,但是翻遍 React Naitve 的官方文档没有发现有详细介绍文件上传的文章,在 github 上搜索一下倒是发现了不少 repo 在做这样的事情

    https://github.com/PhilippKrone/react-native-fileupload

    https://github.com/PhilippKrone/react-native-fileupload

    https://github.com/eduedix/react-native-networking

    https://github.com/eduedix/react-native-networking

    遗憾的是这项项目都是基于 native code 实现的,虽然在 React Native 使用 NativeComment 有很好的工具支持,但是终归没有直接使用纯 JS 的库更方便。其实 React Native 已经悄悄支持了文件上传,我们可以不用引入任何依赖、从容的用几行 JS 来解决文件上的问题。

    ```javascript
    let formData = new FormData();
    formData.append('file', {uri: uri, type: 'application/octet-stream',
    name: key});
    formData.append('key', key);
    formData.append('token', token);
    let options = {};
    options.body = formData;
    options.method = 'post';
    return fetch(conf.UP_HOST, options).then((response) => {
    });
    ```
    

    是不是非常简单直白?上面这几行代码对应到 html 的表单类似这样,我们用这几行代码模拟了一次文件上传的表单

    ```html
    <form method="post" action="http://upload.qiniu.com/"
    enctype="multipart/form-data">
    <input name="key" type="hidden" value="<resource_key>">
    <input name="x:<custom_name>" type="hidden"
    value="<custom_value>">
    <input name="token" type="hidden" value="<upload_token>">
    <input name="file" type="file" />
    <input name="crc32" type="hidden" />
    <input name="accept" type="hidden" />
    </form>
    ```
    

    对应的 Http 请求类似如下

    ```
    POST / HTTP/1.1
    Host: upload.qiniu.com
    Content-Type: multipart/form-data; boundary=<frontier>
    Content-Length: <multipartContentLength>
    --<frontier>
    Content-Disposition: form-data; name="token"
    <uploadToken>
    --<frontier>
    Content-Disposition: form-data; name="key"
    <key>
    --<frontier>
    Content-Disposition: form-data; name="<xVariableName>"
    <xVariableValue>
    --<frontier>
    Content-Disposition: form-data; name="crc32"
    <crc32>
    --<frontier>
    Content-Disposition: form-data; name="accept"
    <acceptContentType>
    --<frontier>
    Content-Disposition: form-data; name="file";
    filename="<fileName>"
    Content-Type: application/octet-stream
    Content-Transfer-Encoding: binary
    <fileBinaryData>
    --<frontier>--
    ```
    

    下面详细分析一下这几行简短的代码的作用,

    ```javascript
    let formData = new FormData();
    formData.append('file', {uri: uri, type: 'application/octet-stream',
    name: key});
    formData.append('key', key);
    formData.append('token', token);
    ```
    

    我们首先创建一个 FormData 对象, FormData 对象指代一个 Form 表单对象的内容,这里我们只需要关注 Form 内的内容和他们的 Content-Type 就可以了,这里我们添加了 key , token 和 file 这几个 form 内容,并特别指定了 file 的 Content-Disposition 的类型是 application/octet-stream ,代表这是一个任意的二进制数据,这里可以是 png,jpeg 的图片和其他任何类型。 key 和 token 是服务器需要的额外信息。 key 是上传到服务器的文件名, token 是请求令牌。最后在用 fetch 提交请求的过程中 FormData 会帮助我们添加 Http 本身的 Content-Type 等信息。

    let options = {};
    options.body = formData;
    options.method = 'post';
    return fetch(conf.UP_HOST, options).then((response) => {
    });
    

    这四行简短的代码帮助我们构建了 Http 的 Payload ,并实际提交到上传的服务器。实际返回的服务器端响应客户端需要处理,可以使用 fetch 的 Promise API ,非常方便

    fetch(url).then((response) => {
    return response.text();
    }).then((responseText) => {
    self.setState({info: responseText});
    }).catch((error) => {
    console.warn(error);
    });
    

    这里有个坑需要提醒一下,大家在 debug 返回响应的时候会发现请求在一个 Promise 结束后不再进入第二个 Promise ,再次发送请求,发现前面请求的 Promise 又继续执行下去。这其实是 React Native Debugger 的一个 bug ,大家忽略就可以了,在非 debug 模式就一切正常了。

    有了以上知识我们就可以很容易的上传文件到服务器了,这里有很多小伙伴会选择类似七牛云存储这种服务来加速开发,根据七牛的官方文档构建 token 就可以上传了,这里七牛也很贴心的推出了 React Native 平台的 SDK

    https://github.com/qiniu/react-native-sdk

    使用 SDK 之后上传文件就变的非常简单了,首先是安装

    $ npm i react-native-qiniu --save
    

    安装之后配置自己的 AK 和 SK ,这个可以在个人中心里面找到

    var qiniu = require('react-native-qiniu');
    qiniu.conf.ACCESS_KEY = <AK>
    qiniu.conf.SECRET_KEY = <SK>
    

    这里我们再举一个现实世界的例子,用户在图片库中选择图片之后上传到七牛云存储,因为目前 React Native 官方还不支持从图片库,摄像头等多个渠道获取图片,这里的代码使用了另外一个开源库 https://github.com/marcshilling/react-native-image-picker

    完整 demo : https://github.com/buhe/present-demo/tree/master/presentdemo

    七牛作为一家紧跟技术趋势的云计算公司,从 React Native 发布以来就对该技术做了持续的跟进,在今年发布了官方版本的存储 SDK ,帮助使用 React Native 创业的小伙伴们更便捷的使用七牛云存储,降低研发成本支出、专注商业模式和技术创新。七牛云将持续发布服务于企业的云服务,也会在第一时间发布 React Native 平台的 SDK ,让使用 React Naitve 的小伙伴在第一时间集成七牛云的最新服务。

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5652 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 09:06 · PVG 17:06 · LAX 01:06 · JFK 04:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.