[ 一起学React系列 -- 8 ] React中的文件上传

终于抽出时间来继续更新自己的博客,最近忙得够呛…
对于该系列博客不知道大家有没有这样的看法,对于React常见的基础东西并没有过多或者详细列出,感觉有点不符合系列标题。的确,笔者一开始也想把React基础从头到尾列一边,但是想想看没这个必要,因为这种基础教程在网上多如牛毛,再写岂不是重复造轮子,所以笔者就挑一些相对“偏僻”但是肯定会用到的东西拿出来分享。

前言

本期的主题是在React中如何实现文件上传。文件上传这个功能在实际开发过程中用的地方相对较多,当然还有很多花里胡哨的解决方案,不过万变不离其宗再复杂的解决方案也离不开最基础的技术,所以笔者将文件上传这一块详细整理了一下并且做了demo供大家参考学习。

文件上传解决方案

目前比较主流的解决方案就是form表单fetch(或者axios)form表单+fetch来实现。对于第二位而言,笔者本着能用原生就用原生的原则就没有使用axios模块。那么下面就一一开始分享。

文件上传解决方案–form表单

利用表单组件进行文件上传是远古时期就一直在用的方法而且还真经久不衰,厉害了。利用form表单的enctype属性可以把表单提交的对象设置为多媒体资源,然后通过inuput:file就可以实现文件上传的功能,例子如下:

import React, {Component} from 'react'

class FormUploadOnly extends Component {
    render() {
        return (
            <div>
                <form action="http://127.0.0.1:3001/file/upload" method="post" enctype="multipart/form-data">
                    <input type="file" name='file'/>
                    <input type="submit" value="上传"/>
                </form>
            </div>
        )
    }
}

export default FormUploadOnly;

这个solution相对便捷有效而且还不用考虑跨域的问题,毕竟我们上传的文件终究还是要访问API接口;不过这种方法还有一个不方便的地方,就是form表单会默认跳转也就是会在浏览器访问你所提交文件的那个接口,这个行为处理起来很麻烦。这个问题笔者推荐通过一个iframe来解决。

文件上传解决方案–fetch

Fetch是浏览器的原生API,可以像Ajax那样请求后台接口。不过因为它是基于Promise的,所以不支持Promise的浏览器则无法使用该方法。闲话不说,如何通过fetch来实现上传?

import React, {Component} from 'react';

class FetchUpload extends Component {
    constructor(props) {
        super(props);
        this.fileInput = React.createRef();
    }

    upload = () => {
        const data = new FormData();
        data.append('file', this.fileInput.current.files[0]);  //相当于 input:file 中的name属性
        fetch('http://127.0.0.1:3001/file/upload', {
            method: 'POST',
            body: data
        }).then(response => console.log(response))
    };
    render() {
        return (
            <div>
                <input type="file" name='file' ref={this.fileInput}/>
                <input type="button" value="上传" onClick={this.upload}/>
            </div>
        )
    }
}
export default FetchUpload;

这个方法比较投机取巧,就是将input:type中的数据append到FormData中,FormData会将数据编译成键值对,这样可以被fetch发送至后台(不仅仅限于fetch,也可以是ajax或者axios等等)。不过这种方法有个致命的问题,那就是会有跨域问题。对于这个问题,笔者会在博客末尾提供相关解决方案。

文件上传解决方案–fetch+form

这个方案看小标题和前面的内容,相信大家都能猜到是什么样子了。下面直接上代码:

import React, {Component} from 'react'

class FormUpload extends Component {
    submit = (e) => {
        e.preventDefault();
        let formData = new FormData(e.target);
        fetch('http://127.0.0.1:3001/file/upload', {
            method: 'POST',
            body: formData //自动将input:file的name属性与文件对象组合成键值对
        }).then(response => console.log(response))
    };

    render() {
        return (
            <div>
                <form onSubmit={this.submit}>
                    <input type="file" name='file'/>
                    <input type="submit" value="上传"/>
                </form>
            </div>
        )
    }
}

export default FormUpload;

总的来说,这个方法和第二中方法在原理上是相同的,只是获取的文件数据不是直接从input:type中获取的,而是从form的提交事件中获取的,其他的没什么变化,所以也会遇到跨域的问题。

后台组成

该博客的demo后台是express写的,所以不管是跨域管理还是接收并保存文件都是基于Node模块。

跨域管理

笔者常用的Node服务跨域解决方案是第三方库cors。当然cors除了是这个第三方库的名字,也有比较重要的W3C标准,它对解决浏览器跨域问题起到重要的作用,不过该博文的重点不在这所以不作赘述,相关的使用方法都在文末的demo里,有兴趣的朋友可以尝试用用,真的很high!

接收并保存文件

因为express自身的request对象不包含上传过来的文件对象,所以必须要用到第三方库multer。负责处理multipart/form-data 类型的表单数据和保存相关资源的作用。

小提示

利用multer初始化一个upload中间件对象时候需要指定一个“标志符”,比如:

let upload = multer({storage: storage}).single('file');

这里的标识符是file,对应前面代码中的:

<input type=”
file” name=’
file‘/>

data.append(‘
file‘, this.fileInput.current.files[0])

这是一个不算大的坑,所以大家使用时候多多关注。

最后笔者奉上准备好的demo,有兴趣的话可以download下来耍耍。当然里面还包含了下一篇文件下载的代码,大家可以也顺带看看。

    原文作者:风吹过的夏夜
    原文地址: https://segmentfault.com/a/1190000016824949
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞