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
随机推荐
- httpPostedFile实现WEBAPI文件上传
public void PostUpload() { var httpPostedFile = HttpContext.Current.Request.Files; foreach(string p ...
- vim 操作命令大全(转)
1. 关于Vim vim是我最喜欢的编辑器,也是Linux下第二强大的编辑器. 虽然emacs是公认的世界第一,我认为使用emacs并没有使用vi进行编辑来得高效. 如果是初学vi,运行一下vimtu ...
- java中 int、char、long各占多少字节数
所谓的占用字节数 就是申请内存的时候所占的空间大小 byte 1字节 最小值是 -128(-2^7): 最大值是 127(2^7-1): boolean 至少1字节 这种类型只作为一 ...
- C#-片段-插入片段:测试
ylbtech-C#-片段-插入片段:测试 using Microsoft.VisualStudio.TestTools.UnitTesting; 1.返回顶部 ·测试方法 [Microsoft.Vi ...
- 简易的CRM系统案例SpringBoot + thymeleaf + MySQL + MyBatis版本
创建maven项目 pop.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns ...
- 一个Flask应用运行过程剖析
相信很多初学Flask的同学(包括我自己),在阅读官方文档或者Flask的学习资料时,对于它的认识是从以下的一段代码开始的: from flask import Flask app = Flask(_ ...
- Qt学习过程
1.常用控件的使用[除了常见的还有QTableWidget.QTreeWidget...]2.信号与槽[需要知道connect函数的最后一个参数Qt::ConnectionType取不同枚举时的含义] ...
- 123457---com.twoapp.shuXueYouXi---小学数学口算
com.twoapp.shuXueYouXi---小学数学口算
- python中的列表推导式——轻量级循环
列表推导式(list comprehension)是利用其他列表创建新列表(类似于数学术语中的集合推导式)的一种方法.它的工作方式类似于for循环,也很简单. 列表推导式书写形式: [表达式 for ...
- Flutter TextField 文本输入框的基本属性及详解
TextField 文本输入框 源码分析: const TextField({ Key key, this.controller, // 控制正在编辑文本 this.focusNode, // 获取键 ...