美文网首页React JSWeb前端之路让前端飞
React学习教程(9)Upload-File&文件上传

React学习教程(9)Upload-File&文件上传

作者: 四冶读史 | 来源:发表于2017-11-01 12:56 被阅读144次

    概述

    React文件上传组件,现代浏览器采用File API+FormData异步上传,兼容IE9使用form+iframe异步上传。(babel6不兼容IE8,如需在IE8使用请再次转换)
    使用到ES6,需要经babel转译。
    IE通过把透明的上传按钮覆盖在传入的children的上传按钮上进行点击的捕捉。同时隐藏iframe。现代浏览器通过传入的按钮上再增加一层wrapper来捕捉。
    丰富的生命周期函数。
    不包含预设样式,开放式组件。
    安装

    npm install --save react-fileupload
    

    引用

    import FileUpload from 'react-fileupload'
    

    添加前端框架bootstrap

    创建Base.js文件,用来引用css文件

    /***
    *
    * bootstrap basic css files and icon file
    * auther
    *
    */
    import './css/bootstrap.min.css';
    import './css/font-awesome.min.css';
    import './css/reacttest.min.css';
    

    css文件和字体文件,自行去官网下载。
    bootstrap:http://getbootstrap.com/2.3.2/
    bootstrap:中文官网http://www.bootcss.com/
    font:http://www.bootcss.com/p/font-awesome/

    添加路由

      <Router>
          <div>
              <Route exact path="/" component={Main} />
              <Route path="/clock" component={Clock} />
              <Route path="/exchangerate" component={ExchangeRate} /> 
              <Route path="/uploadfile" component={UploadFile} /> 
          </div>     
      </Router>
    

    例子

    简单例子

    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    
    // 引用Upload File组件
    import FileUpload from 'react-fileupload';
    
    class UploadObject extends Component{
        render(){
         /*指定参数*/
         var options={
          baseUrl:'http://127.0.0.1/upload/',
          param:{
           _c: 'file',
                    _a: 'UploadFile'
         }
        }
         /*调用FileUpload,传入options。然后在children中*/
         /*传入两个dom(不一定是button)并设置其ref值。*/
         return (
          <FileUpload options={options}>
           <button ref="chooseBtn">choose</button>
           <button ref="uploadBtn">upload</button>
          </FileUpload>
        )         
       }
    }
    

    点击choose,弹出文件选择框,选中文件,再点击upload,则会将文件以POST方式传递到baseUrl,且还传递了2个参数_c和_a。
    后台获取POST来的参数,以python为例

    _c = request.GET['_c']
    _a = request.GET['_a']
    files = request.FILES.get(key, None)
    

    key是键值,类似input中的name,但此处并非如此,而是fileFieldName属性值,文件添加到formData时,默认用file.name作为key,传入string会直接使用此string作为key,若为func则取返回值,func的参数为对应的file对象。如果文件获取不到,则为None
    比如,上传的文件名为test.txt,那么获取文件为:

    files = request.FILES.get(key, None)
    

    完整例子

    /**
     * 文件上传
     */
    import React, { Component } from 'react';
    import ReactDOM from 'react-dom';
    ​
    // 引用Upload File组件
    import FileUpload from 'react-fileupload';
    import './Base.js';
    /**
     * 进度条组件
     */
    function ProgressBar(props){
        return (
            <div>
                <div className="progress" style={{background:'#EDEDED',height:'11px',width: props.width + '%',borderRadius: '4px'}}>
                    <div className="progress-bar" role="progressbar" aria-valuenow={props.width} aria-valuemin="0" aria-valuemax="100" style={{borderRadius: '4px 0 0 4px',width: props.width + '%'}} ></div>
                </div>
                <div className="progress">
                    <div className="progress-bar" role="progressbar" aria-valuenow={props.width} aria-valuemin="0" aria-valuemax="100" style={{width: props.width + '%'}} >
                       {props.width}%
                    </div>
                </div>
            </div>
       );
    }
    
    class UploadObject extends Component{
        constructor(props){
            super(props);
            this.options={
                baseUrl:  "http://localhost:8000/uploadfile/",
                dataType : 'json',
                // enctype: 'multipart/form-data',
                param:{
                    bucket: 'bucketname',
                    prefix: ''
               },
                chooseAndUpload : true,
                multiple: true,
                numberLimit: this.number_limit,
                fileFieldName: this.file_field_name,
                // paramAddToField : {purpose: 'save'},
                beforeChoose: this.before_choose,
                chooseFile: this.choose_file,
                beforeUpload: (files, mill) => {this.before_upload(files,mill);},
                doUpload: this.do_upload,
                onabort: this.onabort,
                uploading: (progress) => {this.uploading(progress);},
                uploadSuccess: this.upload_success,
                uploadError: this.uploadError,
                uploadFail: this.upload_fail
           }
    
            // 绑定
            this.render_progress_bar = this.render_progress_bar.bind(this);
       }
        // 限制上传文件数
        number_limit(){
            return 5;
       }
    
        // 文件键值
        file_field_name(file){
            return file.name;
       }
    
        // 点击上传按钮前调用
        before_choose(){
            return true;
       }
    
        // 选择文件后调用
        choose_file(files){
            console.log(files);
            console.log('you choose',typeof files === 'string' ? files : files[0].name)
       }
    
        // 点击上传按钮前执行的操作
        before_upload(files,mill){
            // 检验上传文件的合法性
            let file_check = true;
            for(var i = 0; i < files.length; i++){
                const file = files[i];   
                if(file.size > (50 * 1024 * 1024)) {
                    // 文件过大
                    console.log('"' + file.name + '"' + "超过" + 50 + "M");
                    file_check = false;
                    break;
               }      
           }
            return file_check;
       }
    
        do_upload(files,mill,xhrID){
            //  
       }
    
        // 在你主动取消一个xhr后触发
        onabort(mill,id){
            //  
       }
    
        // 在文件上传中的时候,浏览器会不断触发此函数,IE9-为虚拟的进度
        uploading(progress){
            //
            console.log(this);
            console.log('loading...', progress.loaded / progress.total + '%');
            this.render_progress_bar(parseInt(100 * (progress.loaded / progress.total)));
       }
        
        // 上传成功后执行的回调(针对AJAX而言)
        upload_success(resp){
            //  
            if(resp.res === 0){
                // 上传成功
                console.log('上传成功');
                console.log(resp);
           }
       }
    ​
        //   上传错误后执行的回调(针对AJAX而言)
        upload_error(err){
            //  
            console.log('上传错误');
            console.log(err);
       }
    
        upload_fail(resp){
            //  
            console.log('上传失败');
            console.log(resp);
       }
    
        /**
         * 渲染进度条
         */
        render_progress_bar(width){
            ReactDOM.render(
                <ProgressBar width={width} />,
                document.getElementById('progerss-bar-id')
           );
       }
    
        componentDidMount(){
          // this.render_progress_bar();
       }
    
        render(){
            /*指定参数*/
            return(
                <div>
                    <FileUpload options={this.options}> 
                        <button ref="chooseAndUpload" className="btn btn-primary">上传文件</button>
                    </FileUpload>
                </div>
           );
       }
    }
    
    class UploadFile extends Component{
        render_upload_file(){
            ReactDOM.render(
                <UploadObject />,
                document.getElementById('upload-file-id')
           );
       }
        componentDidMount(){
            this.render_upload_file();
       }
    
        render(){
            return(
                <div>
                  <div id="upload-file-id"></div>
                  <div id="progerss-bar-id"></div>
                </div>
           );
       }
    }
    export default UploadFile;
    

    chooseAndUpload : true, // 选择文件后,立即上传
    multiple: true, // 支持多文件上传
    numberLimit: this.number_limit, // 多文件上传限制数
    fileFieldName: this.file_field_name, // 以文件名作为键值,在此处其实是可以省略的
    uploading: (progress) => {this.uploading(progress);},
    此处用到了箭头函数,为什么要这么用呢?因为我需要在uploading中渲染进度条,如果不使用箭头函数,那么在uploading这个函数中使用this,这个this指的就是option这个对象了(没错,this变了),如果使用箭头函数,uploading这个函数中的this指的还是UploadObject(这正是我所要的)。
    为什么会这样?
    因为箭头函数没有this,这个this是其父级的this。
    那么文件POST之后,后台是怎么获取的呢?

    服务器端获取文件

    以python为例,以下是一个详细的例子:

    # 上传文件
    # @param   key     
    # @param   path   文件路径
    # @param   bucket_name
    # @method   POST
    #
    def ao_upload_file_post(request):
        data = {}
        data['res'] = status2code.API_ABNORMA
        try:    
            #   通过POST方法获取传递过来的文件
            while True:
                #   接受POST
                if request.method != 'POST':
                    data['res'] = status2code.REQ_PARAM_ERROR
                    break
    
                #   获取参数
                bucket_name = request.GET['bucket']
                prefix     = request.GET['prefix']
    ​
                #   检查参数
                if bucket_name == '':
                    data['res'] = status2code.REQ_PARAM_ERROR
                    break
                
                #   上传文件
                is_ok = True
                for f in request.FILES:
                    key = prefix + request.FILES[f].name
                    is_ok = client2leofs.do_put_object(key, bucket_name, request.FILES[f])
                    if not is_ok:
                        break
                
                #   判断文件上传是否成功
                if not is_ok:
                    pass
                    break
    
                #   上传成功
                data['res'] = status2code.SUCCESS
                break
        except:
            pass
    
        return ao_add_response_access(data)
    

    通过for循环,遍历request.FILES,逐个获取文件。

    注意:
    1.笔者用的是"react": "^15.6.1";
    2.当用"react": "^16.0.0"时报错,错误提示如下:

    “Element ref was specified as a string (ajax_upload_file_input) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).”

    目前尚未解决!

    注:
    本教程相关的所以源码,可在https://github.com/areawen2GHub/reacttest.git下载

    参考地址:
    https://github.com/SoAanyip/React-FileUpload
    https://www.npmjs.com/package/react-fileupload
    http://blog.csdn.net/u011413061/article/details/51946425

    相关文章

      网友评论

      • 权仔:请问现在有解决方法吗?
        四冶读史:已有好久没搞React了,文件上传组件似乎没有更新这么快,建议降低React版本。
      • 简中藏书:目前在用"react": "^16.4.1",引用了这个文件上传组件,一直报错。看到这篇文章最后的提示,已经哭晕在厕所。:toilet:
        简中藏书:@权仔 之前查了一下这个组件很久没有更新,所以放弃了。后来改用ant.design了。
        权仔:请问现在有解决方法吗?
        四冶读史:应该是版本的问题,该文件上传组件应该不支持16.4.1,网上搜下是否有新版本了,或者换个组件。

      本文标题:React学习教程(9)Upload-File&文件上传

      本文链接:https://www.haomeiwen.com/subject/xbnwpxtx.html