精华内容
下载资源
问答
  • zphupload.js 文件上传拖拽效果,以及图片,MP3,MP4格式的预览,ajax上传
  • 只有把图片移到input的区域中才能上传,有什么方式能控制图片上传的拖动放置区域? <code><!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title&...
  • 由于项目需要上传文件到服务器,于是便在文件上传的基础上增加了拖拽上传。拖拽上传当然属于文件上传的一部分,只不过在文件上传的基础上增加了拖拽的界面,主要在于前台的交互, 从拖拽的文件中获取文件列表然后...

    由于项目需要上传文件到服务器,于是便在文件上传的基础上增加了拖拽上传。拖拽上传当然属于文件上传的一部分,只不过在文件上传的基础上增加了拖拽的界面,主要在于前台的交互,

    从拖拽的文件中获取文件列表然后调用上传方法即可。拖拽上传能给用户多一种选择,提高用户体验,下面是最简单的一个推拽上传示例

    HTML部分:

    <!--拖拽上传区域-->
    <div class="dropBox_wrap">
    <div id="dropbox" class="drop">
    <h4>请将文件拖拽到此区域进行上传</h4>
    </div>
    </div>

     

    Js部分:

    //监听拖拽放置区域拖拽事件
    (function(){
        var oDrop = document.getElementById("dropbox"),oBody =           document.querySelector("body");
        EventUtil.addHandler(oDrop,"dragenter",function(evt){
            EventUtil.preventDefault(evt);
        });
        EventUtil.addHandler(oDrop,"dragover",function(evt){
            $(oDrop).addClass("drop_enter").removeClass("drop_leave");
            EventUtil.preventDefault(evt);
        });
        EventUtil.addHandler(oBody,"drop",function(evt){
            $(oDrop).removeClass("drop_leave drop_enter");
            EventUtil.preventDefault(evt);
            return false;
        });
        EventUtil.addHandler(oDrop,"dragleave",function(evt){
            $(oDrop).addClass("drop_leave").removeClass("drop_enter");
            leaveHandler(evt);
        });
        EventUtil.addHandler(oDrop,"drop",function(evt){
            dropHandler(evt);
        });
    })();
    
    function dropHandler(e){
        e.preventDefault();  //阻止默认
        var fileList = e.dataTransfer.files; //获取拖拽的文件列表
    
        if(fileList.length>0&&fileList[0].type != ""){//如果为多个文件,遍历
            var formData = new FormData();  
            for(var i=0;i<fileList.length;i++){
                formData.append('files', fileList[i]);//存入formData对象
            }
            ajaxFileUpload(formData); //调用文件上传方法,可以参考博客内另一篇文章:文件上传,下方会放链接
        }else{
            alert("请上传单个或多个文件");
        }
    
    }
    
    //定义拖拽离开事件
    function leaveHandler(e){
    
    }
    });
    
    //定义事件处理程序方法
    //element:dom对象,type:事件类型,handler:事件处理方法
    var EventUtil = {
        //添加事件监听方法
        addHandler: function(element, type, handler){ 
            if (element.addEventListener){ 
                element.addEventListener(type, handler, false); 
            } else if (element.attachEvent){ 
                element.attachEvent("on" + type, handler); 
            } else { 
                element["on" + type] = handler;
            } 
        }, 
    
        //移除事件监听方法
        removeHandler: function(element, type, handler){ 
            if (element.removeEventListener){ 
                element.removeEventListener(type, handler, false); 
           } else if (element.detachEvent){ 
                element.detachEvent("on" + type, handler); 
           } else { 
            element["on" + type] = null; 
            } 
        },
    
        //获取事件对象
        getEvent: function(event){ 
            return event ? event : window.event;
        },
        getTarget: function(event){ 
            return event.target || event.srcElement; 
        }, 
    
        //阻止默认行为
        preventDefault: function(event){
            if (event.preventDefault){ 
                event.preventDefault(); 
            } else { 
                event.returnValue = false; 
            } 
        }, 
        //阻止事件冒泡
        stopPropagation: function(event){ 
            if (event.stopPropagation){ 
                event.stopPropagation(); 
            } else { 
                event.cancelBubble = true; 
            } 
        }
    }                        

     

     文件上传方法:ajaxFileUpload(formData) 可以参考 文件上传 文章中的方法,传送门=>>

     

    转载于:https://www.cnblogs.com/websharehome/p/8622094.html

    展开全文
  • 主要介绍了MVC文件上传支持批量上传拖拽及预览文件内容校验功能,需要的朋友可以参考下
  • 文件上传 拖拽选择文件 切片上传 断点续传 异步任务并发数控制 秒传

    效果展示

    在这里插入图片描述

    技能栈

    vue+ts+element+egg

    拖拽选择文件

    <div ref="drag" id="drag">
          <input type="file" @change="handleChange" />
    </div>
    
    #drag {
      height: 100px;
      line-height: 100px;
      border: 2px dashed #eee;
      text-align: center;
    }
    
    async mounted() {
     this.bindEvents()
    }
    bindEvents() {
      const drag: any = this.$refs.drag;
      //鼠标拖到上面,注意清除默认事件
      drag.addEventListener("dragover", (e: any) => {
        drag.style.borderColor = "red";
        e.preventDefault()
      });
      //鼠标移开
      drag.addEventListener("dragleave", (e: any) => {
        drag.style.borderColor = "#ccc";
        e.preventDefault()
      });
      //鼠标拖到上面放开
      drag.addEventListener("drop", (e: any) => {
        const fileList: Array<any> = e.dataTransfer.files;
        this.file = fileList[0];
        drag.style.borderColor = "#ccc";
        e.preventDefault()
      });
    }
    

    在这里插入图片描述

    文件类别判断

    一般上传文件,我们只是简单的通过后缀名的方式去识别,但是很容易通过篡改后缀名而越过。一般文件转为十六进制之后,相同文件的首部和尾部的几个字节是固定的,可以依靠这个去进行识别。

    //文件内容转为16进制
    async blobToString(blob) {
     return new Promise(resolve => {
       const reader = new FileReader();
       reader.onload = function() {
         const ret = reader.result.split('')
           .map(v => v.charCodeAt()) //返回指定位置的字符的 Unicode 编码
           .map(v => v.toString(16).toUpperCase())
           .map(v => v.padStart(2, "0")) //字符串补全
           .join(" ")
         resolve(ret)
       };
       reader.readAsBinaryString(blob);
     });
    }
    //判断是否为png图片
    async isPng(file) {
     const ret = await this.blobToString(file.slice(0, 8));
     const isPng = ret == "89 50 4E 47 0D 0A 1A 0A";
     return isPng;
    }
    //判断是否为jpg图片
    async isJpg(file){
     const len = file.size
     const start = await this.blobToString(file.slice(0, 2))
     const tail = await this.blobToString(file.slice(-2, len))
     const isJpg = (start == 'FF D8') && (tail == 'FF D9')
     return isJpg
    }
    //通过文件流来判断文件格式是否符合
    async isImage(file) {
     return await this.isPng(file) || await this.isJpg(file)
    }
    
    //对所选文件进行判断
    if(!await this.isImage(this.file)){
        this.$alert("文件格式不对")
        return
    }else{
        console.log("文件格式正确")
    }
    

    普通上传

    不考虑文件大小,选择文件之后通过formData方式上传,可以配上进度条

    <h2>文件上传</h2>
    <div ref="drag" id="drag">
      <input type="file" @change="handleChange" />
    </div>
    <div>上传进度条</div>
    <div>
        <el-progress :stroke-width='20' :text-inside="true" :percentage="uploadProgress"></el-progress>
    </div>
    

    vue前端

    file: any
    uploadProgress: any = 0
    //文件整体上传
    const form = new FormData()
    form.append('name','file')
    form.append('file',this.file)
    http.post('/uploadFile',form,{
      onUploadProgress:progress=>{
        this.uploadProgress = Number(((progress.loaded/progress.total)*100).toFixed(2))
      }
    })
    

    egg后端处理FormData文件
    这块可详细参考这儿(egg上传文件处理案例

    const fs = require('fs')
    const path = require('path')
    const pump = require('mz-modules/pump')
    async uploadFile(){
    	//整体上传
        const stream = await this.ctx.getFileStream();
        const filename = encodeURIComponent(stream.fields.name) + path.extname(stream.filename).toLowerCase();
        const target = path.join(this.config.baseDir, 'app/public', filename);
        const writeStream = fs.createWriteStream(target);
        await pump(stream,writeStream)
        this.message("上传成功")
    }
    

    文件过大之后,采用整体上传的方式是极不友好的,很慢而且容易失败,失败之后还需要重新来过,故而有了下面几种方式去解决。

    切片上传

    文件切片处理
    切片,即将大文件切割为多个小块

    const CHUNK_SIZE = 50*1024//小块代销
    //切片处理
    createFileChunk(file,size=CHUNK_SIZE){
      const chunks = []
      let cur = 0
      while(cur<file.size){
        chunks.push({index:cur,file:file.slice(cur,cur+size)})
        cur+=size
      }
      return chunks
    }
    

    切片完成之后,我们需要给这些切片起上名称,顺序标记好,否则后端在切片合成的时候极容易乱掉。这块将使用文件的(md5加密hash值+切片顺序)作为名称。
    大文件的hash计算也是一项费时的操作,这儿提供了三种方式去解决。
    webWorker方式
    利用js的新特性,重新启动一个线程去计算hash,则不会影响主线程。不过在vue项目中,新建的worker需要放置于静态文件目录下。
    webWorker用法
    在这里插入图片描述
    主线程

    //webWorker计算hash
    async calculateHashWorker(){
     return new Promise(resolve => {
       const worker = new Worker('/hash.js')
       worker.postMessage({chunks:this.chunks})
       worker.onmessage = e => {
         const {progress,hash} = e.data
         this.hashProgress = Number(progress.toFixed(2))
         if(hash){
           resolve(hash)
         }
       }
     })
    }
    

    hash.js子线程

    //引入spark-md5计算hash
    self.importScripts('spark-md5.min.js')
    self.onmessage = e => {
        //接收主线程传递的数据
        const {chunks} = e.data
        const spark = new self.SparkMD5.ArrayBuffer()
        let progress = 0
        let count = 0
        const loadNext = index => {
            const reader = new FileReader()
            reader.readAsArrayBuffer(chunks[index].file)
            reader.onload = e => {
                count ++
                spark.append(e.target.result)
                if(count==chunks.length){
                    self.postMessage({
                        progress:100,
                        hash:spark.end()
                    })
                }else{
                    progress += 100/chunks.length
                    self.postMessage({
                        progress
                    })
                    loadNext(count)
                }
            }
        }
        loadNext(0)
    }
    

    requestIdleCallback方式
    借鉴react的fiber架构(利用浏览器帧与帧之间的空闲时间)计算hash
    但是这块可能会阻塞到其他函数内部回调函数的执行,打乱执行顺序,具体看这儿

    import sparkMD5 from 'spark-md5'
    async calculateHashIdle(){
      const chunks = this.chunks
      return new Promise(resolve => {
        const spark = new sparkMD5.ArrayBuffer()
        let count = 0
        const appendToSpark = async file=>{
          return new Promise(resolve=>{
            const reader = new FileReader()
            reader.readAsArrayBuffer(file)
            reader.onload = e=>{
              spark.append(e.target.result)
              resolve()
            }
          })
        }
        const workLoop = async deadline=>{
          //timeRemaining获取当前帧的剩余时间
          while(count<chunks.length && deadline.timeRemaining()>1){
            //空闲时间,且有任务
            await appendToSpark(chunks[count].file)
            count++
            if(count<chunks.length){
              this.hashProgress = Number(
                ((100*count)/chunks.length).toFixed(2)
              )
            }else{
              this.hashProgress = 100
              resolve(spark.end())
            }
          }
          window.requestIdleCallback(workLoop)
        }
    
        window.requestIdleCallback(workLoop)
      })
    }
    

    抽样Hash
    首尾全要,中间取部分去计算hash。这儿遇到的问题是有可能不同文件hash相同,比如有些图片仅有几像素内容不同

    import sparkMD5 from 'spark-md5'
    async calculateHashSample(){
      return new Promise(resolve=>{
        const spark = new sparkMD5.ArrayBuffer()
        const reader = new FileReader()
        const file = this.file
        const size = file.size
        const offset = 2*1024*1024
        //第一个2M,最后一个区数据块全要
        const chunks = [file.slice(0,offset)]
        let cur = offset
        while(cur<size){
          if(cur+offset>=size){
            //最后一个区块
            chunks.push(file.slice(cur,cur+offset))
          }else{
            //中间区块,取前中后各2个字节
            const mid = cur+offset/2
            const end = cur+offset
            chunks.push(file.slice(cur, cur+2))
            chunks.push(file.slice(mid, mid+2))
            chunks.push(file.slice(end-2,end))
          }
          cur+=offset
        }
        reader.readAsArrayBuffer(new Blob(chunks))
        reader.onload = e=>{
          spark.append(e.target.result)
          this.hashProgress = 100
          resolve(spark.end())
        }
      })
    }
    

    切片文件上传

    <template>
      <div class="upload">
        <div>计算hasn进度条</div>
        <div>
            <el-progress :stroke-width='20' :text-inside="true" :percentage="hashProgress"></el-progress>
        </div>
        <el-button type="primary" @click="uploadFile">上传</el-button>
        <div>切片上传进度条</div>
        <!-- chunk.progress 
          progress<0 报错 显示红色
          == 100 成功
          别的数字 方块高度显示 -->
          <!-- 尽可能让方块看起来是正方形
          比如10各方块 4*4
          9 3*3
          100 10*10 -->
        <div v-if="chunks" class="cube-container" :style="{width:cubeWidth+'px'}">
          <div class="cube" v-for="chunk in chunks" :key="chunk.name">
            <div 
              :class="{
                'uploading':chunk.progress>0&&chunk.progress<100,
                'success':chunk.progress==100,
                'error':chunk.progress<0
              }"
              :style="{height:chunk.progress+'%'}"
            >
              <i class="el-icon-loading" style="color:#f56c6c" v-if="chunk.progress<100&&chunk.progress>0"></i>
            </div>
          </div>
        </div>
      </div>
    </template>
    <style lang="scss">
    .cube-container{
      .cube{
        width:14px;
        height:14px;
        line-height:12px;
        border:1px solid #000;
        background: #eee;
        float: left;
        .success{
          background: green;
        }
        .uploading{
          background: blue;
        }
        .error{
          background: red;
        }
      }
    }
    </style>
    
    //上传整体进度
    get uploadProgress(){
        if(!this.file){
            return 0
        }
        const loaded = this.chunks.map(item=>item.chunk.size*item.progress)
                                  .reduce((acc,cur)=>acc+cur,0)
        return parseInt(((loaded*100)/this.file.size).toFixed(2))
    }
    async uploadFile() {
        const chunks = this.createFileChunk(this.file)
        // const hash = await this.calculateHashWorker()
        // const hash = await this.calculateHashIdle()
        const hash = await this.calculateHashSample()
        this.hash = hash
        //切片处理
        this.chunks = chunks.map((chunk,index)=>{
            const name = hash+'-'+index
            return {
                hash,
                name,
                index,
                chunk:chunk.file
            }
        })
        await this.uploadChunks()
      }
      //上传切片
      async uploadChunks(){
        const requests = this.chunks
          .map((chunk,index)=>{
            const form = new FormData()
            form.append('chunk',chunk.chunk)
            form.append('hash',chunk.hash)
            form.append('name',chunk.name)
            return {form,index:chunk.index,error:0}
          })
          .map(({form,index})=>
         	   http.post('/uploadFile',form,{
               onUploadProgress:progress=>{
                 //每个区块有自己的进度条,整体的进度条需要计算
                 this.chunks[index].progress = Number(((progress.loaded/progress.total)*100).toFixed(2))
               }
             }))
        //发起批量请求
        await Promise.all(requests)
        //所有切片上传完毕,通知后台进行切片内容合并,生成完整图片
        await this.mergeRequest()
      }
      //切片合并
      async mergeRequest(){
        const ret = await http.post('/mergeFile',{
          ext:this.file.name.split('.').pop(),
          size:CHUNK_SIZE,
          hash:this.hash
        })
      }
    

    egg后端处理

    const fs = require('fs')
    const fse = require('fs-extra')
    const path = require('path')
    //处理上传的切片
    async uploadFile(){
        const {ctx} = this
        //切片上传
        const file = ctx.request.files[0]
        const {hash,name} = ctx.request.body
        /*
        config.default.js
        //文件存储目录
      	config.UPLOAD_DIR = path.resolve(__dirname,'..','app/public')
        */
        const chunkPath = path.resolve(this.config.UPLOAD_DIR,hash)
        if(!fse.existsSync(chunkPath)){
            //创建目录
            await fse.mkdir(chunkPath)
        }
        //将文件移入目录
        await fse.move(file.filepath,`${chunkPath}/${name}`)
        this.message('切片上传成功')
    }
    //整合切片
    async mergeFile(){
      const {ext,size,hash} = this.ctx.request.body
       const filePath = path.resolve(this.config.UPLOAD_DIR,`${hash}.${ext}`)
       await mergeFileCont(filePath,hash,size)
       this.success({
           url:`/public/${hash}.${ext}`
       })
    }
    async mergeFileCont(filePath,fileHash,size){
    	//读取public存放的切片文件夹
       const chunkDir = path.resolve(this.config.UPLOAD_DIR,fileHash)
        let chunks = await fse.readdir(chunkDir)
        chunks.sort((a,b)=>a.split('-')[1] - b.split('-')[1])
        chunks = chunks.map(cp => path.resolve(chunkDir, cp))
        await this.mergeChunks(chunks, filePath, size)
    }
    async mergeChunks(files, dest, size){
    	//将切片流生成新的图片
        const pipStream = (filePath, writeStream) => new Promise(resolve => {
            const readStream = fse.createReadStream(filePath)
            readStream.on('end', () => {
                fse.unlinkSync(filePath)
                resolve()
            })
            readStream.pipe(writeStream)
        })
        await Promise.all(
            files.forEach((file,index) => {
                pipStream(file, fse.createWriteStream(dest, {
                    start: index * size,
                    end: (index + 1) * size
                }))
            })
        )
    }
    

    异步任务并发控制

    上面切片上传采用promise.all的方式将所有异步任务同时触发,请求过多可能会造成页面假死、阻塞流程、报错等问题。
    这儿限定每次请求只能进行固定次数的异步任务,每请求成功一个则新增一个,确保并发的数量固定。此外,针对可能的报错失败重试三次的操作,若重试三次都报错,则流程终止。

    //上传切片
    async uploadChunks(){
      const requests = this.chunks
        .map((chunk,index)=>{
          const form = new FormData()
          form.append('chunk',chunk.chunk)
          form.append('hash',chunk.hash)
          form.append('name',chunk.name)
          return {form,index:chunk.index,error:0}
        })
      //发起批量请求
      await sendRequest(requests)
      //所有切片上传完毕,通知后台进行切片内容合并,生成完整图片
      await this.mergeRequest()
    }
    async sendRequest(chunks){
     return new Promise((resolve,reject)=>{
       const len = chunks.length
       //限制每次最多只能同时发起4次请求
       let limit = len > 4 ? 4 : len
       let counter = 0
       let isStop = false
       const start = async () => {
         if(isStop) return
         const task = chunks.shift()
         if(task){
           const {form,index} = task
           try{
             await http.post('/uploadFile',form,{
               onUploadProgress:progress=>{
                 this.chunks[index].progress = (((progress.loaded/progress.total)*100).toFixed(2))
               }
             })
             if(counter==len-1){
               resolve()
             }else{
               counter++
               //启动下一个任务
               start()
             }
           }catch(e){
             this.chunks[index].progress = -1
             if(task.error<3){
               task.error++
               chunks.unshift(task)
               start()
             }else{
               //错误三次
               isStop = true
               reject()
             }
           }
         }
       }
       while(limit>0){
         //启动limit个任务
         //模拟下延迟任务
         setTimeout(()=>{
           start()
         },Math.random()*2000)
         limit-=1
       }
     })
    }
    

    断点续传

    大文件上传,中间如因网络或其他原因使得上传出错,重新去上传是一个很麻烦的事情。在上文切片上传的基础上,中间出错但是之前的切片内容已保存到后台,这时可以通过发起判断,后台返回已上传的切片列表。再次上传的时候,只需要过滤掉已经上传过的,只传未上传的就好。

    async uploadFile() {
    	//断点续传,判断切片是否已存在
        const ret = await http.post('/checkFile',{
          hash:this.hash,
          ext:this.file.name.split('.').pop()
        })
        //uploaded为上传结果
        //uploadedList为已上传切片列表
        const {uploaded,uploadedList} = ret.data.data
        //切片上传
        this.chunks = chunks.map((chunk,index)=>{
            const name = hash+'-'+index
            return {
                hash,
                name,
                index,
                chunk:chunk.file,
                //这块加入已上传判断,使得已上传过的进度条可以直接显示出来
                progress:uploadedList.indexOf(name)>-1?100:0
            }
        })
        await this.uploadChunks(uploadedList)
    }
    //上传切片
      async uploadChunks(uploadedList=[]){
        const requests = this.chunks
          //这块过滤掉已经上传的切片
          .filter(chunk=>uploadedList.indexOf(chunk.name)==-1)
          .map((chunk,index)=>{
            const form = new FormData()
            form.append('chunk',chunk.chunk)
            form.append('hash',chunk.hash)
            form.append('name',chunk.name)
            return {form,index:chunk.index,error:0}
          })
        await this.sendRequest(requests)
        await this.mergeRequest()
      }
    

    egg后台处理

    const fs = require('fs')
    const fse = require('fs-extra')
    const path = require('path')
    async checkFile(){
         const {ext,hash} = this.ctx.request.body
         const filePath = path.resolve(this.config.UPLOAD_DIR,`${hash}.${ext}`)
         let uploaded = false
         let uploadedList = []
         if(fse.existsSync(filePath)){
             //文件存在
             uploaded = true
         }else{
             uploadedList = await this.getUploadedList(path.resolve(this.config.UPLOAD_DIR,hash))
         }
         this.success({
             uploaded,
             uploadedList
         })
     }
     async getUploadedList(dirPath){
         return fse.existsSync(dirPath)
             ?(await fse.readdir(dirPath)).filter(name=>name[0]!=='.')
             :[]
     }
    

    秒传

    即在上传之前,先调取后端接口,判断是否文件已存在,若存在直接提示秒传成功。

    async uploadFile() {
    	//断点续传,判断切片是否已存在
        const ret = await http.post('/checkFile',{
          hash:this.hash,
          ext:this.file.name.split('.').pop()
        })
        //uploaded为上传结果
        //uploadedList为已上传切片列表
        const {uploaded,uploadedList} = ret.data.data
        if(uploaded){
          return this.$alert('秒传成功')
        }
    }
    
    展开全文
  • 文件拖拽上传

    2019-05-25 17:37:21
    一个简单的文件异步拖拽上传 DOM结构:  文件拖拽上传我们只需要在HTML中写一个容器就可以可以根据我们额实际需求可以改成长的圆的方的~,结构如下: <div id="demo"></div>  拖拽脚本实现: ...

     一个简单的文件异步拖拽上传

    DOM结构:

      文件的拖拽上传我们只需要在HTML中写一个容器就可以可以根据我们额实际需求可以改成长的圆的方的~,结构如下:

    <div id="demo"></div>

      拖拽脚本实现:

      拖拽的实现主要依赖三个事件和两个对象

      三个事件:ondragenter、ondragover、ondrop

      两个对象:FormData、XMLHttpRequest

      直接在目标元素上绑定事件对象,进行一些逻辑处理,代码如下:

    window.onload = function(){
            var demo = document.getElementById("demo");
            demo.addEventListener("dragenter",handler,false);
            demo.addEventListener("dragover",handler,false);
            demo.addEventListener("drop",upload,false);
            function upload(e){
                var e = e || window.event;
                handler(e);
                var files = e.dataTransfer.files;
                for(var i=0,il=files.length;i<il;i++){
                    sendFile(files[i]);
                }
            }
            function sendFile(file){
                var xhr = null;
                if(window.XMLHttpRequest){
                    xhr = new XMLHttpRequest();
                }else{
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                }
                var fd = new FormData();
                fd.append("myFile",file);
                xhr.open("post","URL",true)
                xhr.send(fd)
                xhr.onreadystatechange = function(){
                    if(xhr.readyState==4&&xhr.status==200){
                        alert(xhr.responseText)
                    }
                }
            }
            function handler(e){
                var e = e || window.event;
                e.preventDefault ? e.preventDefault(): e.returnValue = false;
                e.stopPropagation ? e.stopPropagation(): e.cancelBubble = true;
            }
        }
    展开全文
  • 虽然IE的落后让很多开发者还在观望中,但是Gmail邮箱的附件拖拽功能已经给部分用户带来了极大的方便,而需要大量上传文件的CMS(内容管理系统)也将会从中受益。让我们看一下Firefox 是如何使用拖拽上传功能的:首先...

    通过HTML的文件API ,Firefox、Chrome等浏览器已经支持从操作系统直接拖拽文件,并上传到服务器。

    相对于使用了十多年的HTML表单,这是一个革命性的进步。虽然IE的落后让很多开发者还在观望中,但是Gmail邮箱的附件拖拽功能已经给部分用户带来了极大的方便,而需要大量上传文件的CMS(内容管理系统)也将会从中受益。

    让我们看一下Firefox 是如何使用拖拽上传功能的:

    首先提供一个区域来放置文件

    Html代码

    然后监听拖拽过程中的dragenter、dragleave、drop等事件

    Js代码

    document.addEventListener("dragenter", function(e){

    dropbox.style.borderColor = 'gray';

    }, false);

    document.addEventListener("dragleave", function(e){

    dropbox.style.borderColor = 'silver';

    }, false);

    dropbox.addEventListener("dragenter", function(e){

    dropbox.style.borderColor = 'gray';

    dropbox.style.backgroundColor = 'white';

    }, false);

    dropbox.addEventListener("dragleave", function(e){

    dropbox.style.backgroundColor = 'transparent';

    }, false);

    dropbox.addEventListener("dragenter", function(e){

    e.stopPropagation();

    e.preventDefault();

    }, false);

    dropbox.addEventListener("dragover", function(e){

    e.stopPropagation();

    e.preventDefault();

    }, false);

    dropbox.addEventListener("drop", function(e){

    e.stopPropagation();

    e.preventDefault();

    handleFiles(e.dataTransfer.files);

    submit.disabled = false;

    }, false);

    其中最主要的是drop事件中用handleFiles()依次处理所有文件

    Js代码

    handleFiles = function(files) {

    for (var i = 0; i < files.length; i++) {

    var file = files[i];

    }

    }

    对于图片类型的文件可以直接读取内容,显示预览图

    Js代码

    if (!file.type.match(/image*/)) {

    continue;

    }

    var img = document.createElement("img");

    img.classList.add("obj");

    img.file = file;

    preview.appendChild(img);

    var reader = new FileReader();

    reader.onload = (function(aImg) { return function(e) { aImg.src = e.target.result; }; })(img);

    reader.readAsDataURL(file);

    接下来就是核心功能:ajax上传。首先新建一个XHR请求

    Js代码

    var xhr = new XMLHttpRequest();

    xhr.open('post', '/file/upload', true);

    监听上传进度和完成事件

    Js代码

    xhr.upload.addEventListener("progress", function(e) {

    if (e.lengthComputable) {

    var percentage = Math.round((e.loaded * 100) / e.total);

    img.style.opacity = 1-percentage/100.0;

    }

    }, false);

    xhr.upload.addEventListener("load", function(e){

    }, false);

    最后把数据模拟成multipart/form-data的格式上传

    Js代码

    xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary); // simulate a file MIME POST request.

    xhr.setRequestHeader("Content-Length", fileSize);

    var body = '';

    body += "--" + boundary + "\r\n";

    body += "Content-Disposition: form-data; name=\""+dropbox.getAttribute('name')+"\"; filename=\"" + fileName + "\"\r\n";

    body += "Content-Type: "+fileType+"\r\n\r\n";

    body += fileData + "\r\n";

    body += "--" + boundary + "--\r\n";

    xhr.sendAsBinary(body);

    展开全文
  • 拖拽上传: <style> .wrap{ background: #e5e5e5; width:500px; height:100px; margin: 0 auto; text-align: center; line-height:100px; color: #fff; } #box{ height:500px; borde
  • silverlight4 文件支持拖拽上传
  • 文件拖拽上传

    2019-09-21 13:38:42
    在项目中碰到的多文件的拖拽上传总结。 html代码部分:<body> <div class="fileBox"> <p class="fileInputP vm"> <i>选择文件<...-- 选中单个文件上传 --> ...
  • 文件上传(拖拽文件)

    千次阅读 2018-05-22 00:52:24
    本地上传,提前预览(图片,视频)1.html中div标签预览显示,button标签触发上传事件。...将图片拖拽到此&lt;/div&gt; &lt;button onclick="xhr2()"&gt;ajax上传&lt;/
  • 本地上传,提前预览(图片,视频) 1.html中div标签预览显示,button标签触发上传事件。 ... &lt;div id="drop_area"...将图片拖拽到此&lt;/div&gt;  &lt;button oncl...
  • 文件拖拽上传插件

    2018-08-02 10:07:39
    文件拖拽,上传插件,dropzone实现拖放文件上传并预览图片
  • 支持拖拽上传JS文件上传代码是一款上传完毕后可获取到图片名称和上传状态,高级浏览器支持拖拽上传需开启配置,兼容IE7及以上版本浏览器。
  • 本篇内容主要解决.net core中文件上传的问题 开发环境:ubuntu+vscode.本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
  • Java实习生一枚,前端知识薄弱,最近因为工作需要,做了一个拖拽文件上传的功能,发现dropzone.js挺不错的,特地做个笔记。自己写的拖拽文件至一个按钮上传的功能,前端及java代码如下:jsp页面:1. 首先必须引入...
  • 文件拖拽上传效果

    2014-08-14 10:56:09
    纯种js 文件拖拽上传效果,下载后点击自己看源码。我也是从网上找的。如果不是你想要的请见谅。
  • 拖拽上传文件

    2013-03-07 16:09:04
    目前采用拖拽上传得网站有:Gmail,网易邮箱,酷盘,还有一些国外的网站,用户只要拖拽文件到指定区域就可以轻松实现文件上传,技术难度为0,用户体验良好!
  • 具有多文件上传拖拽、进度条和图像预览功能的文件上传插件,支持跨域、分块、暂停恢复和客户端图像缩放。可与任何服务端平台(如PHP、Python、Ruby on Rails、Java、Node.js、Go等)一起使用,支持标准的HTML表单...
  • 文件上传拖拽

    2014-09-22 23:49:00
    上传文件 目前网页上传分为三种: 1、form提交 2、flash上传 3、插件上传 各有利弊,form提交就是没进度条,不太好取消。flash比较难搞的就是在非ie中...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,142
精华内容 856
关键字:

文件上传拖拽