zoukankan      html  css  js  c++  java
  • 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{
       200px;
      height: 100px;
      border: 1px solid green;
      display: none;
     }
     #img{
       50px;
      height: 50px;
      display: none;
     }
     #upimg{
      text-align: center;
      font: 8px/10px '微软雅黑','黑体',sans-serif;
       300px;
      height: 10px;
      border: 1px solid green;
     }
     #load{
       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);
    

      

      

  • 相关阅读:
    设计模式-可复用面向对象软件基础笔记
    C#--笔记
    win系统下nodejs安装及环境配置
    三步将Node应用部署到Heroku上 --转载
    Ubuntu 重启命令
    Ubuntu ssh免密登录
    Ubuntu Hadoop环境搭建(Hadoop2.6.5+jdk1.8.0_121)
    Ubuntu vim使用
    Scala学习——array与arraybuffer的区别(初)
    Scala学习——可变参数(初)
  • 原文地址:https://www.cnblogs.com/muamaker/p/7660990.html
Copyright © 2011-2022 走看看