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
随机推荐
- git前期准备
git小结 设置用户名 git config –global user.name 'itcast' 设置用户名邮箱 git config –global user.email 'itcast' 查看设 ...
- equal numbers
给你一个n长度的数组,让你修改0到n次,问每次修改后能剩下不同个数的最小数是多少: 这里有了两种做法,一种是变成他们的lcm这样的话,修改后答案应该是减去改过的个数然后在加一 另一种就是数字修改成序列 ...
- arcgis python 更新日期为随机数
#coding:UTF-8 import time import random import re # 根据时间返回,返回随机年月日时间 def getRandomDate(sYear, sMonth ...
- LC 456. 132 Pattern
Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence ai, aj, ak such that ...
- 命令行启动python的IDLE
如果你电脑上使用了anaconda2,默认路径为python2,但是你又想使用anaconda2下的python3的idle 方法如下: 首先查看python的路径: (deeplearning3) ...
- Qt编写控件属性设计器9-数据库采集
一.前言 数据库作为数据源,在很多组态软件中使用非常多,指定数据库类型,填写好数据库连接信息,指定对应的数据库表和字段,采集间隔,程序按照采集间隔自动采集数据库数据,绑定到界面上的控件赋值显示即可.使 ...
- php利用文件进行排他型锁定,防止并发
<?php $fp = fopen('/tmp/file.lock', "a+"); if(flock($fp, LOCK_EX)) { //进行排他型锁定 fwrite($ ...
- Jquery Ajax WebService
仅供参考 ws.aspx 代码 <script type="text/javascript"> $(function () { $.ajax({ type: " ...
- 动态绑定easyui datagrid列名
根据实时数据在同一个DataGrid中显示不同字段,本身easyui并没有支持动态绑定列名,只有show属性显示或隐藏某字段.今天在网上看到直接修改easyui类库动态绑定列名的方法,废话不多说直接借 ...
- 第六章 Realm及相关对象——《跟我学Shiro》
转发地址:https://www.iteye.com/blog/jinnianshilongnian-2022468 目录贴:跟我学Shiro目录贴 6.1 Realm [2.5 Realm]及[3. ...