React学习教程(9)Upload-File&文件上传
概述
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