0.效果
实现了文件上传,删除,查看。查看功能是使用一个iframe打开一个上传文件之后,后端返回的网页。
1.安装相关库
下载的JS文件中包含各个版本的webuploader,可以按需引入。本项目仅需使用到的源文件为webuploader.css,webuploader.js、Uploader.swf
(2)js源文件放在react项目public目录下,然后index.html的script标签引入js文件。注意:由于webuploader使用了jquery,所以必须在webuploader之前引入jquuery
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> <link rel="stylesheet" href="./webuploader2/webuploader.css"> <script type="text/javascript" src="./webuploader2/webuploader.js"></script>
(3)在index.html中将WebUploader挂载到window对象
<script type="text/javascript"> console.log('WebUpLoader',WebUploader); window.WebUploader = WebUploader </script>
(4)在组件中使用WebUploader
const WebUploader = window.WebUploader
(5)项目使用antd UI,安装
npm i antd
2.webuploader组件
下面是react中的完整代码
import React, { Component } from 'react'
import { Card, Button, Row, Col, Divider, Progress, message, Modal } from 'antd';
import { nanoid } from 'nanoid'
import './index.scss'
import $ from 'jquery'
const WebUploader = window.WebUploader
export default class WebUpLoaderCom extends Component {
uploader = null
state = {
uploader: {},
fileList: [], // file: {},把file对象也保存下来 progress ,status: 'START',statusName: '待开始',
//上传的文件列表 file.id file.name
showIframe: false,
}
componentDidMount() {
let that = this
var startDate;
// HOOK 这个必须要再uploader实例化前面
WebUploader.Uploader.register({
// 在文件发送之前执行
'before-send-file': 'beforeSendFile',
// 在文件分片后,上传之前执行(如果没有启用分片,整个文件被当成一个分片)
'before-send': 'beforeSend',
// 在文件所有分片都上传完后,且服务端没有错误返回后执行
"after-send-file": "afterSendFile"
}, {
beforeSendFile: function (file) {
startDate = new Date();
console.log("开始上传时间" + startDate)
// console.log("beforeSendFile");
// Deferred对象在钩子回掉函数中经常要用到,用来处理需要等待的异步操作。
var deferred = WebUploader.Deferred();
//1、计算文件的唯一标记MD5,用于断点续传
uploader.md5File(file, 0, 3 * 1024 * 1024).progress(function (percentage) {
// 文件md5编码的进度
}).then(function (val) { // 完成
console.log('File MD5 Result:', val);
file.md5 = val;
file.uid = WebUploader.Base.guid();
// 判断文件是否上传过,是否存在分片,断点续传
$.ajax({
type: "POST",
url: "http://ip:port/site_survey/bigfile/check",
async: false,
data: {
fileMd5: val
},
success: function (data) {
var resultCode = data.code;
// 秒传
if (resultCode === -1) {
// 文件已经上传过,忽略上传过程,直接标识上传成功;
uploader.skipFile(file);
file.pass = true;
} else {
//文件没有上传过,下标为0
//文件上传中断过,返回当前已经上传到的下标
file.indexcode = resultCode;
}
}, error: function () {
}
});
//获取文件信息后进入下一步
deferred.resolve();
});
return deferred.promise();
},
beforeSend: function (block) {
//获取已经上传过的下标
var indexchunk = block.file.indexcode;
var deferred = WebUploader.Deferred();
if (indexchunk > 0) {
if (block.chunk > indexchunk) {
//分块不存在,重新发送该分块内容
deferred.resolve();
} else {
//分块存在,跳过
deferred.reject();
}
} else {
//分块不存在,重新发送该分块内容
deferred.resolve();
}
//返回Deferred的Promise对象。
return deferred.promise();
},
afterSendFile: function (file) {
//如果所有分块上传成功,则通知后台合并分块
$.ajax({
type: "POST",
url: "http://ip:port/site_survey/bigfile/merge",
data: {
fileName: file.name,
fileMd5: file.md5
},
success: function (data) {
console.log('data', data);
}, error: function (error) {
console.log('error', error);
}
});
}
});
// 实例化
var uploader = WebUploader.create({
pick: {
id: '#picker',
label: '点击选择文件'
},
duplicate: true,//去重, 根据文件名字、文件大小和最后修改时间来生成hash Key
swf: 'js/Uploader.swf',
chunked: true,//开启分片
chunkSize: 10 * 1024 * 1024, // 10M 每个分片的大小限制
threads: 3,
server: 'http://ip:port/site_survey/bigfile/upload',
auto: false,
// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
disableGlobalDnd: true,
fileNumLimit: 1024,
fileSizeLimit: 50 * 1024 * 1024 * 1024,//50G 验证文件总大小是否超出限制, 超出则不允许加入队列
fileSingleSizeLimit: 10 * 1024 * 1024 * 1024 //10G 验证单个文件大小是否超出限制, 超出则不允许加入队列
});
that.setState({ //把实例保存到state中
uploader: uploader
})
// 当有文件被添加进队列的时候
uploader.on('fileQueued', function (file) {
let appendFile = that.state.fileList;
let res = appendFile.some(item => {
return item.file.name === file.name
})
if (res) {
message.error(file.name + '文件重复。')
return
}
appendFile.push({
file: file, //把file对象也保存下来
fileId: file.id,
progress: 0,
status: 'START',
statusName: '待开始',
})
that.setState({
fileList: appendFile,
})
});
uploader.onUploadBeforeSend = function (obj, data) {
// console.log("onUploadBeforeSend",data);
var file = obj.file;
data.md5 = file.md5 || '';
data.uid = file.uid;
};
// 上传中 用来记录上传进度
uploader.on('uploadProgress', function (file, percentage) {
let updateFileList = that.state.fileList;
let res = updateFileList.map(item => { //文件上传中时更新文件状态和进度条
if (item.file.id === file.id) {
item.progress = Math.floor(percentage * 100);
item.status = "UPLOADING";
item.statusName = "上传中";
}
return item
})
that.setState({
fileList: res,
})
});
// 上传成功后执行 失败不执行
uploader.on('uploadSuccess', function (file) {
console.log('suc file', file);
let updateFileList = that.state.fileList;
let res = updateFileList.map(item => { //文件上传成功更新状态
if (item.file.id === file.id) {
item.progress = 100;
item.status = "UPLOADED";
item.statusName = "已完成"
}
return item
})
that.setState({
fileList: res,
})
var endDate = new Date();
var time = (endDate - startDate) / 1000 + "s"
var text = '已上传';
if (file.pass) {
text = "文件秒传功能,文件已上传。"
}
message.success(`${file.name} ${text},文件上传耗时${time}`)
});
//上传失败执行
uploader.on('uploadError', function (file) {
message.error(`${file.name}上传出错`)
});
//上传完成后 无论成功失败都执行
uploader.on('uploadComplete', function (file) {
});
}
//上传文件
up = (item) => {
return () => {
const { uploader, fileList } = this.state;
uploader.upload(item.file)
let updateObj = fileList;
let idx = fileList.indexOf(item);
updateObj[idx].status = "UPLOADING";
updateObj[idx].statusName = "上传中";
this.setState({ fileList: updateObj })
}
}
//删除上传的文件
removeFile = (item) => {
return () => {
const { uploader, fileList } = this.state;
uploader.removeFile(item.file)
let idx = fileList.indexOf(item);
fileList.splice(idx, 1);
this.setState({ fileList })
}
}
display = (item) => {
return () => {
this.setState({ showIframe: true })
}
}
handleCancel = () => {
this.setState({ showIframe: false })
}
render() {
const { fileList, showIframe } = this.state
console.log('showIframe', showIframe);
return (
<div className='webuploader'>
<Card title="三维地形文件列表" className='audio-file-list'
extra={<div id="picker">选择大文件</div>}
>
{
fileList.map((item, index) => {
return (
<Row key={nanoid()}>
<Col span={8}>
<p className='audio-file-list'>{item.file.name}</p>
</Col>
<Col span={6}>
<Progress percent={item.progress === undefined ? '0' : item.progress} />
</Col>
<Divider type="vertical" />
<Col span={3}>
<Button type="dashed" onClick={this.up(item)}>开始上传</Button>
</Col>
<Divider type="vertical" />
<Col span={2}>
<Button type="dashed" onClick={this.removeFile(item)}>删除</Button>
</Col>
<Divider type="vertical" />
<Col span={2}>
<Button type="dashed" onClick={this.display(item)}>查看</Button>
</Col>
</Row>
)
})
}
</Card >
<Modal width={'90%'} className='modal' title="现场建模" visible={showIframe} footer={null} onCancel={this.handleCancel}>
<iframe className='iframe' id='iframe' title='三维地形' src="http://fex.baidu.com/webuploader/document.html"></iframe>
</Modal>
</div>
)
}
}
代码中使用jquery,先安装
npm install jquery --save
removeFile
- removeFile( file, true ) ⇒ undefined 移除某一文件, 默认只会标记文件状态为已取消,如果第二个参数为
true
则会从 queue 中移除。如果需要删除文件,代码如下:
$("#removeFile").click(function () { console.log('bf files',uploader.getFiles()) uploader.removeFile(file,true); console.log('af files',uploader.getFiles()) });
注:在操作的时候你会发现 删除前后files文件列表 一样,其实只不过是记录存在而已,已经从queue删除了。此时,点击上传删除的文件已经没响应。
注意
(*)webuploader中需要使用let that = this 先暂存component对象 然后webuploader register upload等方法里面 this指代webuploader对象。
(*)不可以直接修改file对象
3.修改webuploader UI
WebUploader只包含文件上传的底层实现,不包括UI部分。所以交互方面可以自由发挥。
4.参考资料
在React中使用WebUploader实现大文件分片上传的踩坑日记! - 杰哥斯坦森 - 博客园 函数式组件中使用WebUploader