1,下载WebUploader并在react的index.html中引入,当然还有jquery
<script src="/statics/jquery.js"></script>
<script src="/statics/webuploader-0.1.5/webuploader.js"></script>
这样就可以在react中用window.WebUploader获取到WebUploader了
2,react部分
const $ = window.$;
const WebUploader = window.WebUploader;
constructor(props) {
super(props);
this.state = {
uploadPercent: 0, //上传进度
tips: ''//提示
};
this.uploader = '';//存储创建后的WebUploader对象
this.blockInfo = [];//分片信息存储
}
componentDidMount() {
this.createUploader();
}
createUploader() {
WebUploader.Uploader.register(
{
"before-send": "beforeSend"
},
{
beforeSend: (block) => {
console.log('检测分片是否上传');
let deferred = WebUploader.Deferred();
let chunk = block.chunk;
if ($.inArray(chunk.toString() + '.part', this.blockInfo) >= 0) {
console.log("已有分片.正在跳过分片" + block.chunk.toString() + '.part');
deferred.reject();
} else {
deferred.resolve();
}
return deferred.promise();
}
}
);
//配置可直接查看官网
this.uploader = WebUploader.create({
swf: '/webuploader-0.1.5/Uploader.swf',
server: '/file/upload',
auto: false,
chunked: true,
chunkSize: 1 * 1024 * 1024,
fileSizeLimit: 2 * 1024 * 1024 * 1024,
fileSingleSizeLimit: 2 * 1024 * 1024 * 1024,
resize: false,
accept: {
title: 'mp4,jpg',
extensions: 'mp4,jpg',
mimeTypes: 'video/mp4,image/jpg'
},
chunkRetry: false,
threads: 1,
fileNumLimit: 1,
//附加数据
formData: {
guid: WebUploader.Base.guid('hzk_'),
id: '100'
}
});
//这里我用的是单文件上传,所以每次在文件入列之前,重置uploader和分片信息
this.uploader.on('beforeFileQueued', (file) => {
this.uploader.reset();
this.blockInfo = [];
});
//文件切面
this.uploader.on('fileQueued', (file) => {
this.uploader.md5File(file, 0, 4 * 1024 * 1024)
.progress((percentage) => {
this.setState({
tips: '正在读取文件...' + percentage.toFixed(2) * 100 + '%'
});
})
.then((fileMd5) => {
let formData = this.uploader.option('formData');
formData.md5 = fileMd5;
this.uploader.option('formData', formData);
let fileInfo = {
name: file.name,
size: file.size,
type: file.type,
ext: file.ext
};
//下面这个方法是一个验证分片是否存在的请求,如果存在的话直接续传
_mm.request({
type: 'post',
url: '/file/uploadMd5check',
data: {
...fileInfo,
...formData
}
}).then(({data, msg}) => {
switch (data.code) {
// 断点
case '0':
this.setState({
tips: '正在从上次断开的地方上传文件...'
});
for (let i in data.blockInfo) {
this.blockInfo.push(data.blockInfo[i]);
}
file.status = 0;
break;
// 无断点
case '1':
this.setState({
tips: '正在上传文件...'
});
file.status = 1;
break;
}
this.uploader.upload();
}, msg => {
this.setState({
tips: <span style={{color: 'red'}}>{msg}</span>
});
})
});
});
//所有分片上传完成后
this.uploader.on('uploadSuccess', (file, response) => {
let formData = this.uploader.option('formData');
let fileInfo = {
name: file.name,
size: file.size,
type: file.type,
ext: file.ext
};
this.setState({
tips: '正在验证文件...'
});
//请求合并
_mm.request({
type: 'post',
url: '/file/uploadMerge',
data: {...formData, ...fileInfo}
}).then(({data, msg}) => {
this.setState({
tips: <span style={{color: 'green'}}>{msg}</span>
});
}, msg => {
this.setState({
tips: <span style={{color: 'red'}}>{msg}</span>,
uploadPercent: 0
});
})
});
//进度处理
this.uploader.on('uploadProgress', (file, percentage) => {
this.setState({
uploadPercent: percentage,
uploading: true
})
});
this.uploader.on('error', (type) => {
switch (type) {
case 'Q_EXCEED_NUM_LIMIT':
alert('一次只能上传一个文件');
break;
case 'Q_EXCEED_SIZE_LIMIT':
alert('文件大小超过限制');
break;
case 'Q_TYPE_DENIED':
alert('文件格式只能是video/mp4');
break;
}
});
//创建上传按钮
this.uploader.addButton({
id: '#picker',
multiple: false
});
}
//render一个简单的进度条和上传提示信息
render() {
return (<div>
<div style={{height: "3px", background: "#EFEFEF", width: "400px"}}>
<div style={{height: "3px", background: "#1890ff", width: this.state.uploadPercent * 400 + 'px'}}></div>
</div>
<span>
{this.state.tips}
{this.state.uploadPercent > 0 ? (this.state.uploadPercent * 100).toFixed(2) + '%' : ''}
</span>
<span id={"picker"}>上传视频</span>
</div>)
}
3,服务端php
我这里使用的是phalcon框架,相信phper应该能看懂
private function validUpload() {
$ext = pathinfo($this->request->getPost('name'),PATHINFO_EXTENSION);
$type = $this->request->getPost('type');
$size = $this->request->getPost('size');
if (! in_array(strtolower($ext), ['mp4']) || !in_array(strtolower($type), ['video/mp4'])) {
$this->di->get('util')->R(Err::ERR_RESPONSE_VALID, '文件类型错误');
}
$maxSize = 2 * 1024 * 1024 * 1024;
if ($size > $maxSize) {
$this->di->get('util')->R(Err::ERR_RESPONSE_VALID, '文件太大');
}
}
/**
* 分片上传前验证
* 返回上传临时文件夹内是否有已经上传的分片
* @return void
*/
public function uploadMd5checkAction()
{
$this->validUpload();
$md5 = $this->request->getPost('md5');
$dir = $this->di->get('config')->application->uploadTempDir;
$dir = $dir . $md5;
if (file_exists($dir)) {
$blockInfo = $this->getUploadTempFile($dir);
if (count($blockInfo) > 0) {
//下面这个函数R的功能是response一个类似{"errCode":0,"msg":"","data":{"code":0,"blockInfo":"$blockInfo"}}的json
$this->di->get('util')->R(0, '', ["code"=>"0" , 'blockInfo' => $blockInfo]);
} else {
$this->di->get('util')->R(0, '', ["code"=>"1"]);
}
} else {
@mkdir ($dir,0777,true);
$this->di->get('util')->R(0, '', ["code"=>"1"]);
}
}
/**
* 上传分片
* 分片上传附带参数中必须要有验证所需的信息,否则不予上传
* @return void
*/
public function uploadAction()
{
$this->validUpload();
$file = $_FILES;
$md5 = $this->request->getPost('md5');
$dir = $this->di->get('config')->application->uploadTempDir;
$dir = $dir . $md5;
//md5文件夹已经在上传前验证的时候新建了,如果不存在则不上传
if (!file_exists($dir)) {
die('error');
}
// 移入缓存文件保存
$chunk = $this->request->getPost('chunk') . '.part';
move_uploaded_file($file["file"]["tmp_name"], $dir.'/' . $chunk);
}
/**
* 文件验证合并
* 简单验证:分片必须连续
* @return void
*/
public function uploadMergeAction() {
$md5 = $this->request->getPost('md5');
//临时文件夹
$dir = $this->di->get('config')->application->uploadTempDir;
$dir = $dir . $md5;
$blockInfo = $this->getUploadTempFile($dir);
//排序
natsort($blockInfo);
//验证文件分片是否连续
for($i = 0; $i < count($blockInfo); $i++) {
$chunkName = $i . '.part';
if ( ! in_array($chunkName, $blockInfo)) {
$this->di->util->R(4, '文件验证错误,上传失败');
}
}
//新文件名
$ext = pathinfo($this->request->getPost('name'),PATHINFO_EXTENSION);
$filename = uniqid(mt_rand(), true) . '.' . $ext;//生成唯一的字符串作为文件名
$uploadDir = $this->di->get('config')->application->uploadDir . date('Y') . '/';
if (! is_dir($uploadDir) && ! mkdir($uploadDir)) {
$this->di->util->R(4, '文件夹创建失败');
}
$uploadFile = $uploadDir . $filename;
//新建新文件
if (!$out = @fopen($uploadFile, "wb")) {
$this->di->get('util')->R(4, '系统错误,文件未打开');
}
//把所有的分片写入文件,写入前给这个文件加个锁
if (flock($out, LOCK_EX)) {
foreach ($blockInfo as $b) {
if (!$in = @fopen($dir.'/'.$b, "rb")) {
break;
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
@fclose($in);
@unlink($dir.'/'.$b);
}
flock($out, LOCK_UN);
}
@fclose($out);
@rmdir($dir);
//$uploadFile就是最终上传完成的文件
$this->di->get('util')->R(0, '上传成功');
}
9d134eba-0823-418e-b933-0c473a37f647.gif