js文件分段上传
前端代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN">
<head>
<title>分割大文件上传</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
#test{
width: 200px;
height: 100px;
border: 1px solid green;
display: none;
}
#img{
width: 50px;
height: 50px;
display: none;
}
#upimg{
text-align: center;
font: 8px/10px '微软雅黑','黑体',sans-serif;
width: 300px;
height: 10px;
border: 1px solid green;
}
#load{
width: 0%;
height: 100%;
background: green;
text-align: center;
}
</style>
</head>
<body>
<form enctype="multipart/form-data" action="file.php" method="post">
<!--
<input type="file" name="pic" />
<div id="img"></div>
<input type="button" value="uploadimg" onclick="upimg();" /><br />
-->
<div id="upimg">
<div id="load"></div>
</div>
<input type="file" name="mof" multiple="multiple"/>
<input type="button" value="uploadfile" onclick="upfile();" />
<input type="submit" value="submit" />
</form>
<div id="test">
测试是否DIV消失
</div>
<script type="text/javascript"> var xhr=null; if (window.XMLHttpRequest)
{// code for all new browsers
xhr=new XMLHttpRequest();
}
else if (window.ActiveXObject)
{// code for IE5 and IE6
xhr=new ActiveXObject("Microsoft.XMLHTTP");
} if(xhr == null){
alert("Your browser does not support XMLHTTP."); } if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
} else {
alert('The File APIs are not fully supported in this browser.');
} var fd;
var des=document.getElementById('load');
var file;
const LENGTH=2*1024*1024;
var start;
var end;
var blob;
var pecent;
var filename;
//var pending;
//var clock;
function upfile(){
start=0;
end=LENGTH+start;
//pending=false; file=document.getElementsByName('mof')[0].files[0];
//filename = file.name;
if(!file){
alert('请选择文件');
return;
}
//clock=setInterval('up()',1000);
up(); } function up(){
/*
if(pending){
return;
}
*/
if(start<file.size){
xhr.open('POST','file.php',true);
//xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.onreadystatechange=function(){
if(this.readyState==4){
if(this.status>=200&&this.status<300 || this.status == 304){
if(this.responseText.indexOf('failed') >= 0){
if(this.responseText.indexOf('success') >= 0){
alert('文件发送成功');
}else{
alert('文件发送失败,请重新发送');
des.style.width='0%';
} }else{
start=end;
end=start+LENGTH;
setTimeout('up()',100);
} }
}
}
xhr.upload.onprogress=function(ev){
if(ev.lengthComputable){
pecent=100*(ev.loaded+start)/file.size;
if(pecent>100){
pecent=100;
}
//num.innerHTML=parseInt(pecent)+'%';
des.style.width=pecent+'%';
des.innerHTML = parseInt(pecent)+'%'
}
}
//分割文件核心部分slice
if(file.slice){
blob=file.slice(start,end);
}else if (file.webkitSlice) {
blob = file.webkitSlice(start, stop + 1);
} else if (file.mozSlice) {
blob = file.mozSlice(start, stop + 1);
}else{
alert('不支持slice上传');
return;
} fd=new FormData();
fd.append('mof',blob);
fd.append('test',file.name);
//console.log(fd);
//pending=true;
xhr.send(fd);
}else{
alert('上传成功');
}
} xhr.onerror = function(e){
console.log(e);
alert('服务器错误');
}
function change(){
des.style.width='0%';
} </script>
</body>
</html>
PHP代码
<?php
/****
waited
****/
//print_r($_FILES);exit; $file = $_FILES['mof']; $type = trim(strrchr($_POST['test'], '.'),'.'); // print_r($_POST['test']);exit; if($file['error']==0){
if(!file_exists('./upload/upload.'.$type)){
if(!move_uploaded_file($file['tmp_name'],'./upload/.'.$type)){
echo 'failed';
}
}else{
$content=file_get_contents($file['tmp_name']);
if (!file_put_contents('./upload/.'.$type, $content,FILE_APPEND)) {
echo 'failed';
}
}
}else{
echo 'failed success';
} ?>
使用md5加密参数
使用es6的封装,支持断点续传
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>文件上传</title>
<script src="https://code.jquery.com/jquery-3.4.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.js"></script>
</head>
<body>
<h1>大文件上传测试</h1>
<div>
<h3>自定义上传文件</h3>
<input id="file" type="file" name="avatar"/>
<div>
<input id="submitBtn" type="button" value="提交">
</div>
</div>
<script type="text/javascript">
//开始插件代码
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
class FlieSlice {
constructor(opt){
let def = {
progress: function(){},
success: function(){},
fail: function(){},
chunkSize:2*1024*1024, // 每个chunk的大小,设置为2兆
userId:"" ,//如果传了用户的 id,通过 hash 记录在 localstorage ,就可以断点续传,不传 userid,则不考虑断点续传
autoUpload:true //自动上传
}; def = Object.assign(def,opt);
def.blockCount = Math.ceil(opt.file.size / def.chunkSize); // 分片总数
this.def = def; this.paused = false;
this.current = 0;
this.iscontinue = false; if(def.autoUpload){
this.send();
}
}
async send(){
try{
this.hash = await this.getHash(); //这个是文件的hash,可以再加上用户id,这样就可以做断点续传
if(this.def.userId){
//需要断点续传的判断
const prevIndex = window.localStorage.getItem(this.hash);
const index = Number(prevIndex);
if(prevIndex && index < this.def.blockCount){
//如果存在,说明之前这个文件没有传完
this.iscontinue = true;
this.upload(index);
}else{
//如果不存在,则不会继续了
this.upload();
}
}else{
this.upload();
} }catch(e){
console.warn(e);
this.paused = true;
}
}
upload(i){
i = i || 0;
const me = this;
const def = me.def;
const {file,blockCount,chunkSize} = def;
const hash = me.hash;
me.current = i; if(i >= blockCount || me.paused){
return ;
}
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
if(start >= end){
return ;
}
console.log(`start:${start}_end:${end},${i}`);
def.progress && def.progress({
name:file.name,
file:blobSlice.call(file, start, end), // 当前的块
done: i >= blockCount-1, // 是否已经发送完
hash:hash, // hash
size:file.size, // 文件的总大小
index:i, // 当前传到了第几块
count:blockCount, // 总块数
iscontinue:me.iscontinue, //这次上,属于断点续传
next(err){ // 下次迭代.
if(err){
me.paused = true;
window.localStorage.removeItem(hash);
return ;
}
++i;
if(i < blockCount){
//每次上传,都记录 hash
window.localStorage.setItem(hash,i);
me.upload(i);
}else{
window.localStorage.removeItem(hash);
def.success && def.success();
}
}
});
}
getHash(){
const me = this;
const {file,blockCount,chunkSize,userId} = me.def;
return new Promise((resolve, reject) => {
let currentChunk = 0;
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
function loadNext() {
const start = currentChunk * chunkSize;
const end = Math.min(file.size, start + chunkSize);
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
fileReader.onload = e => {
spark.append(e.target.result); // Append array buffer
currentChunk += 1;
if (currentChunk < blockCount) {
loadNext();
} else {
const result = spark.end();
// 如果单纯的使用result 作为hash值的时候, 如果文件内容相同,而名称不同的时候
// 想保留两个文件无法保留。所以把文件名称加上。
const sparkMd5 = new SparkMD5();
sparkMd5.append(result);
sparkMd5.append(file.name);
if(userId){
sparkMd5.append(userId);
}
const hexHash = sparkMd5.end();
resolve(hexHash);
}
};
fileReader.onerror = (e) => {
console.warn('文件读取失败!');
reject(e);
};
loadNext();
}).catch(err => {
console.log(err);
});
}
paused(){
//停止上传
this.paused = true;
}
play(){
//开始上传
this.paused = false;
this.upload(this.current);
}
}
//插件代码结束 const submitBtn = $('#submitBtn');
const fileDom = $('#file')[0];
submitBtn.on('click',() => { // 获取到的files为一个File对象数组,如果允许多选的时候,文件为多个
const files = fileDom.files;
const file = files[0];
if (!file) {
alert('没有获取文件');
return;
}
new FlieSlice({
file:file,
userId:"mannymyu",
progress(obj){
console.log(obj); const form = new FormData();
form.append('file', obj.file);
form.append("name",obj.name);
form.append('count', obj.count);
form.append('index', obj.index);
form.append('size', obj.size);
form.append('hash', obj.hash);
form.append("done",obj.done);
$.ajax({
url: 'http://127.0.0.1:3000',
type: 'post',
data: form,
contentType: false,
processData: false,
success: function (res) {
console.info(res);
//在这里 执行自己的 ajax 上传,执行成功后调用 obj.next 执行下次
if(res.code == 200){
obj.next();
}else{
obj.next(res);
}
},
error: function (error) {
console.info(error);
obj.next(error);
}
}) },
success(){
console.log("success");
}
})
})
</script>
</body>
</html>
使用node 的 koa 来完成后端
const os = require('os');
const path = require('path');
const koaBody = require('koa-body');
const Koa = require('koa');
const app = new Koa();
const cors = require('koa2-cors');
const promisify = require("util").promisify;
const fs = require("fs");
const readFile = promisify(fs.readFile);
const fsextra = require('fs-extra')
//递归的创建文件夹
function mkdirs(dirpath) {
if (!fs.existsSync(path.dirname(dirpath))) {
mkdirs(path.dirname(dirpath));
}
fs.mkdirSync(dirpath);
} function createDir(myPath){
fs.existsSync(myPath) == false && mkdirs(myPath);
} //合并文件
function mergeFile(target,arr){
return new Promise((resolve,reject)=>{
function write(curr){
fs.stat(target,function (err,stat) {
if(err){
let WStream = fs.createWriteStream(target);
let readStream = fs.createReadStream(curr);
readStream.pipe(WStream);
run()
}else if(stat.isFile()){
let size = stat.size;
let WSoptions = {
start: size,
flags: "r+"
}
let WStream = fs.createWriteStream(target,WSoptions)
let readStream = fs.createReadStream(curr);
readStream.pipe(WStream);
run()
}else{
reject("不是一个文件");
}
})
}
function run(){
if(arr.length > 0){
write(arr.shift());
}else{
resolve(target);
}
}
run();
});
} const main = async function(ctx) {
const filePaths = [];
const files = ctx.request.files || {};
const params = ctx.request.body; const temp = path.join(__dirname, "tmp" ,params.hash);
const filePath = path.join(temp , `${params.hash}_${params.index}`); createDir(temp);
for (let key in files) {
const file = files[key];
if(Object.prototype.toString.call(file) == '[object Array]'){
ctx.body = {
code:403,
msg:"分片上传不允许多文件上传"
}
return ;
}else{
const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);
let staticDir;
console.log(params);
if(params.done == "true"){
//如果已经完成了---合成文件
staticDir = path.join(__dirname, "static",params.name); //最后文件存的地址
var arr = [];
for(var i = 0; i <= params.index; i++){
arr.push(
path.join(temp , `${params.hash}_${i}`)
);
}
try{
let res = await mergeFile(staticDir,arr);
ctx.body = {
code:200,
data: {
done:true,
path:staticDir
},
msg:"成功"
}; }catch(e){
console.log("错误--",e);
ctx.body = {
code:500,
msg:"合并文件错误"
};
} //删除临时文件
fsextra.remove(temp, err => {
if (err) return console.error("删除文件是失败",err) console.log('删除文件成功!')
});
}else{
ctx.body = {
code:200,
data: {
done:false,
path: filePath
},
msg:"成功"
};
} }
return ;
}
}; app.use(cors());
app.use(koaBody({ multipart: true }));
app.use(main);
app.listen(3000);
js文件分段上传的更多相关文章
- js之大文件分段上传、断点续传
文件夹上传:从前端到后端 文件上传是 Web 开发肯定会碰到的问题,而文件夹上传则更加难缠.网上关于文件夹上传的资料多集中在前端,缺少对于后端的关注,然后讲某个后端框架文件上传的文章又不会涉及文件夹. ...
- 兼容好的JS图片上传预览代码
转 : http://www.codefans.net/articles/1395.shtml 兼容好的JS图片上传预览代码 (谷歌,IE11) <html xmlns="http:/ ...
- 兼容各浏览器的js判断上传文件大小
由于项目需要,在网上找了一个JS判断上传文件大小的程序,经测试兼容IE6-,Firefox10,Opera11.,safari5.,chrome17 <!DOCTYPE html> < ...
- 利用ajaxfileupload.js异步上传文件
1.引入ajaxfileupload.js 2.html代码 <input type="file" id="enclosure" name="e ...
- 怎么通过js获取上传的图片信息(临时保存路径,名称,大小)然后通过ajax传递给后端?
今天在论坛上看到这样一个问题,有必要编辑搜集下. 问题描述:怎么通过js获取上传的图片信息(临时保存路径,名称,大小)然后通过ajax传递给后端 题主用jquery接收 <input name= ...
- js获取上传文件内容(未完待续)
js 获取上传文件的字节数及内容 <div> 上传文件 : <input type="file" name = "file" id = &qu ...
- js判断上传文件大小
下面提供三款网页特效判断上传文件大小哦,这三种方法是现在限制文件上传大小比较好的方法,可以在客户上传文件时限制上传文件大小判断处理<!doctype html public "-//w ...
- BootStrap fileinput.js文件上传组件实例代码
1.首先我们下载好fileinput插件引入插件 ? 1 2 3 <span style="font-size:14px;"><link type="t ...
- js文件上传库
收集了2个与具体UI库和框架无任何耦合的JS文件上传库:支持断点续传.支持npm安装. resumable.js fileapi
随机推荐
- go struct 自定义标签获取
package main import ( "fmt" "reflect" ) type Test struct { Id int `json:"us ...
- Python 自学笔记(八)
import math def A(a,b): print("第一个参数的值为"+str(a)) print("第一个参数的值为"+str(b)) a = 1 ...
- 用户登录之asp.net cookie的写入、读取与操作
页面前面: <div id="login" runat="server"> <span class="log_title" ...
- JavaScript中三个等号和两个等号的区别(“===”与“==”的区别)
1.===:三个等号我们称为等同符,当等号两边的值为相同类型的时候,直接比较等号两边的值,值相同则返回true,若等号两边的值类型不同时直接返回false. 例:100===“100” //返回f ...
- 阶段5 3.微服务项目【学成在线】_day09 课程预览 Eureka Feign_16-课程预览功能开发-接口测试
cms和课程的微服务重启 从数据库内找一条数据 进入到了断点 拼装课程信息 ,然后进行远程调用 抛出异常 可能是开了两个cms服务的事,负载均衡 到了另外一个服务里面 ,关掉一个 把02关掉,重启cm ...
- SpringCloud学习成长之 十 高可用服务注册中心
文章 第一篇: 服务的注册与发现(Eureka) 介绍了服务注册与发现,其中服务注册中心Eureka Server,是一个实例,当成千上万个服务向它注册的时候,它的负载是非常高的,这在生产环境上是不太 ...
- SpringMVC拦截器工作流程图
- tcpdump抓包代码
tcpdump - tcp[:]=:]=0x4854 or tcp 抓出来的包可以导入wireshark分析 以上代码曾经在ios越狱机器上使用,用于抓包,具体也记不起来了 导入wireshark效果
- LeetCode_189. Rotate Array
189. Rotate Array Easy Given an array, rotate the array to the right by k steps, where k is non-nega ...
- mysql使用truncate截断带有外键的表时报错--解决方案
报错内容如:1701 - Cannot truncate a table referenced in a foreign key constraint 一.为什么要使用truncate 使用trunc ...