React JSWeb前端之路让前端飞

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

2017-11-01  本文已影响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

上一篇 下一篇

猜你喜欢

热点阅读