文件下载

传统的文件下载有两种方法:

  1. 使用<a/>标签,href属性直接连接到服务器的文件路径
  2. window.location.href="url"

这两种方法效果一样。但有个很大的问题,如果下载出现异常(连接路径失效、文件不存在、网络问题等),会导致原本的页面被覆盖掉,显示404等错误信息。

大致的优化思路如下:

  1. 使用<a/>标签HTML5新的属性download。
  2. 使用<iframe><iframe/>元素进行下载。
  3. 使用ajax、axios、fetch等方法异步下载。
  4. 使用websocket下载。

我们来逐一分析:

  1.  <a/>标签的download属性,需要和href一起用,download的作用是为下载的文件赋文件名。

    • 如果服务端没有指定文件名,就以此属性规定的名称命名。
    • 如果下载出现异常,该属性的存在能够保证页面不会出问题。
    • 如果服务端返回的不是文件、而是字符,如果download=‘’error.txt”,能够通过打开此文件查看到返回的文本信息。
  2. <iframe>标签可以做到在现有的页面下,内嵌一个子页面。当用户点击文件下载时,将隐藏的iframe元素的src属性指向文件下载路径。
    • 如果没有异常,文件将会直接下载。
    • 如果出现异常,iframe子页面会报错,父页面不会受任何影响。
  3. 使用异步请求进行下载。
    • 在网上看了看,大致的流程是:发送异步请求时设置responseType为blob,即接收流数据为blob对象保存在内存中。接收完成后,生成链接地址(1.通过FileReader对象将blob对象生成base64编码 2.通过URL.createObjectURL生成指向文件内存的链接),写入<a/>标签的href属性,然后模拟点击<a/>按标签实现下载。
    • 此方法最大的问题是,因无法直接操作磁盘,故接收的文件必须先存放在内存中(且只有传输完成后才能构建blob对象),才能转化成文件。因此,大文件的下载可能会把你的浏览器挤爆。
  4. 使用websocket下载。
    • 需要额外开启websocket服务,此方法未做实践。

总结以上方法,最推荐前两种,方便简单。

附上后端Django代码(适用于前两种方法):

def syncDownLoad(request):"文件下载"print("同步下载文件")startTime = time.time()def file_iterator(file, chunk_size=1024):with open(file, "rb") as f:while True:c = f.read(chunk_size)if c:yield celse:endTime = time.time()print("传输时间", endTime - startTime)breakfileRoute = "/static/files/2018/12/18/第四章(1)学习动机概述.mp4"fileName = "第四章(1)学习动机概述.mp4"route = os.path.dirname(os.path.dirname(__file__)) + fileRouteif os.path.exists(route):  # 如果存在文件response = StreamingHttpResponse(file_iterator(route))# response['Content-Type'] = 'application/octet-stream'response['Content-Type'] = 'text/html'response['Content-Disposition'] = 'attachment;filename="{0}"'.format(fileName).encode("utf-8")return responseelse:return HttpResponse("cannot find file")

参考链接:

https://scarletsky.github.io/2016/07/03/download-file-using-javascript/

https://my.oschina.net/watcher/blog/1525962

文件上传

概述

文件上传需要处理的问题有:

1.多文件上传  2.异步上传  3.拖拽上传  4.上传限制(限制大小、类型) 5.显示上传进度、上传速度、中途取消上传  6.预览文件

HTML DEMO

<input type="file" id="file" name="myfile" οnchange="onchanges()" multiple="multiple"/>
<input type="button" οnclick="SerialUploadFile()" value="上传"/>

一、多文件上传

<input type="file" id="file" name="myfile" multiple="multiple"/> <!-- multiple属性 -->

二、异步上传

通过ajax等方式异步上传,FormData对象支持传输文件。

function UploadFile() {var fileObj = document.getElementById("file").files;  // js 获取文件对象(FileList对象)// FormData 对象var form = new FormData();form.append("author", "xueba");             // 可以增加表单数据for (let i = 0; i < fileObj.length; i++){form.append("file", fileObj[i]);        // 文件对象}$.ajax({url: "/file_upload/",type: "POST",async: true,      // 异步上传data: form,contentType: false, // 必须false才会自动加上正确的Content-TypeprocessData: false, // 必须false才会避开jQuery对 formdata 的默认处理。XMLHttpRequest会对 formdata 进行正确的处理success: function (data) {data = JSON.parse(data);data.forEach((i)=>{console.log(i.code,i.file_url);});},error: function () {alert("aaa上传失败!");},});}

三、拖拽上传

默认文本、图像和链接可以被拖动。其它的元素想要被拖动,只需为标签加一个draggable="true"属性

<div draggable="true"><div/>

HTML5 API drag 和 drop

被拖动元素发生的事件dragstart    被拖动元素开始拖动时drag         正在被拖动时dragend      取消拖拽时目标元素发生的事件(当某元素被绑定以下事件就变成了目标元素)dragenter    拖动元素进入目标上触发dragover     拖动元素在目标元素上移动触发dragleave    拖动元素离开目标时触发drop         拖动元素在目标上释放触发,这时不会触发dragleave注意:1.目标元素默认不能够被拖放drop,要在dragover事件中取消默认事件(e.preventDefault())2.有些元素(img)被拖放后,默认以链接形式打开,要在drop事件中取消默认事件(e.preventDefault())【火狐浏览器可能不顶用,需要再加event.stopPropagation()】dataTransfer(事件对象属性(对象))数据交换:只是简单的拖拽没有意义,我们还需要数据交换,即被拖动元素和目标元素之间的数据交换。方法:setData(key,value)  设置数据(key和value都必须是string类型)getData(key)        获取数据clearData()         清除数据(不传参清空所有数据)setDragImage(imgElement,x,y)      设置元素移动过程中的图像(参数:图像元素,xy表示图像内的偏移量)属性:dropEffect  表示被拖动元素可以执行哪一种放置行为(一般在dragover事件内设置)none禁止放置(默认值)   move移动到新的位置   copy复制到新的位置 linkeffectAllowed  用来指定拖动时被允许的行为(一般无需设置)copy,move,link,copyLink,copyMove,linkMove,all,none,uninitialized默认值,相当于all.files    FileList对象。如果拖动的不是文件,此为空列表     items    返回DataTransferItems对象,该对象代表了拖动数据。types    返回一个DOMStringList对象,该对象包括了存入dataTransfer中数据的所有类型。注意:1.如果拖拽了文本,浏览器会自动调用setData(),设置对应文本数据

该功能没有Demo

参考链接:

https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API

https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer

https://www.zhangxinxu.com/wordpress/2018/09/drag-drop-datatransfer-js/

http://www.sohu.com/a/198973397_291052

四、上传限制

<input type="file"  accept="image/*" /> 接收全部格式的图片

此外,获取到的File对象中有type属性可以得知文件类型,size属性的得知文件大小

五、上传进度、上传速度、中途取消上传

原生API

xhr.onload = function(e){};//上传请求完成
xhr.onerror = function(e){};//上传异常
xhr.upload.onloadstart = function(e){};//开始上传
xhr.upload.onprogress =function(e){};//上传进度  这个方法会在文件每上传一定字节时调用e.loaded//表示已经上传了多少byte的文件大小
e.total//表示文件总大小为多少byte
通过这两个关键的属性就可以去计算 上传进度与速度xhr.onreadystatechange = function(){}//当xhr的状态(上传开始,结束,失败)变化时会调用 该方法可以用来接收服务器返回的数据中途取消上传 xhr.abort();

单文件上传 或 多文件串行上传 Demo:(该Demo只会有一个进度条,显示上传总进度。对应“异步上传”的代码)

xhr.upload.addEventListener("progess",progessSFunction,false); // 上传过程中显示进度和速度function progressSFunction(e) {var progressBar = document.getElementById(`pro`);var percentageDiv = document.getElementById(`per`);if (e.lengthComputable) // lengthComputable表示进度信息是否可用{progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[0].last_laoded) / (e.timeStamp - progress[0].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[0].last_laoded = e.loaded, progress[0].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;}}

多文件并行上传进度显示:(多个进度条,分别上传)

// 多文件并行上传function ParallelUploadFile() {last_laoded = 0;last_time = (new Date()).getTime();var fileObj = document.getElementById("file").files;  // js 获取文件对象for (let k = 0; k < fileObj.length; k++){let domStr = `<div> ${fileObj[k].name},大小${fileObj[k].size}字节<progress class='progressBar' id='pro${k}' value='' max=''></progress><span class='percentage' id='per${k}'></span></div>`;$("body").append(domStr);// FormData 对象var form = new FormData();form.append("author", "xueba");             // 可以增加表单数据form.append("csrfmiddlewaretoken", $("[name = 'csrfmiddlewaretoken']").val());form.append("file", fileObj[k]);// XMLHttpRequest 对象{#var xhr = new XMLHttpRequest();#}{#xhr.open("post", "/file_upload/", true);#}{#xhr.onload = function () {#}{#   alert("上传完成!");#}{# };#}{#xhr.upload.addEventListener("progress", progressFunction, false);#}{#xhr.send(form);#}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 异步上传data: form,contentType: false, // 必须false才会自动加上正确的Content-TypeprocessData: false, // 必须false才会避开jQuery对 formdata 的默认处理。XMLHttpRequest会对 formdata 进行正确的处理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", (e) => {progressPFunction(e, k)}, false);xhr.upload.onloadstart = (e) => {progress[k] = {last_laoded: 0,last_time: e.timeStamp,};};xhr.upload.onloadend = () => {delete progress[k];};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上传失败!");},});}}

六、预览文件

预览图片

function onchanges() { // input file绑定onchange事件let files = document.getElementById("file").files;if(files[0].type.indexOf("image")>-1){let read = new FileReader();read.onload = function(e) { // 读取操作完成时触发let img = new Image();img.src = e.target.result; // 将base64编码赋给src属性$("body")[0].appendChild(img);};read.readAsDataURL(files[0]); // 读取文件转化成base64编码}
} 

七、前后端汇总Demo

前端

HTML

<input type="file" id="file" name="myfile" οnchange="onchanges()" multiple="multiple"/>
<input type="button" οnclick="SerialUploadFile()" value="上传"/>

JavaScript

   let progress = {};let last_laoded;let last_time;function onchanges() {let files = document.getElementById("file").files;console.log(`共${files.length}个文件`);let countSize = 0;for (let i = 0; i < files.length; i++) {console.log(`${files[i].name}  大小${files[i].size}`);countSize += files[i].size;}console.log(`共计占用${countSize}字节`);if (files[0].type.indexOf("image") > -1){let read = new FileReader();read.onload = function (e) { // 读取操作完成时触发let img = new Image();img.src = e.target.result; // 将base64编码赋给src属性$("body")[0].appendChild(img);};read.readAsDataURL(files[0]); // 读取文件转化成base64编码}}// 多文件并行上传function ParallelUploadFile() {last_laoded = 0;last_time = (new Date()).getTime();var fileObj = document.getElementById("file").files;  // js 获取文件对象for (let k = 0; k < fileObj.length; k++){let domStr = `<div> ${fileObj[k].name},大小${fileObj[k].size}字节<progress class='progressBar' id='pro${k}' value='' max=''></progress><span class='percentage' id='per${k}'></span></div>`;$("body").append(domStr);// FormData 对象var form = new FormData();form.append("author", "xueba");             // 可以增加表单数据form.append("csrfmiddlewaretoken", $("[name = 'csrfmiddlewaretoken']").val());form.append("file", fileObj[k]);// XMLHttpRequest 对象{#var xhr = new XMLHttpRequest();#}{#xhr.open("post", "/file_upload/", true);#}{#xhr.onload = function () {#}{#   alert("上传完成!");#}{# };#}{#xhr.upload.addEventListener("progress", progressFunction, false);#}{#xhr.send(form);#}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 异步上传data: form,contentType: false, // 必须false才会自动加上正确的Content-TypeprocessData: false, // 必须false才会避开jQuery对 formdata 的默认处理。XMLHttpRequest会对 formdata 进行正确的处理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", (e) => {progressPFunction(e, k)}, false);xhr.upload.onloadstart = (e) => {progress[k] = {last_laoded: 0,last_time: e.timeStamp,};};xhr.upload.onloadend = () => {delete progress[k];};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上传失败!");},});}}// 多文件串行上传function SerialUploadFile() {var fileObj = document.getElementById("file").files;  // js 获取文件对象let domStr = `<div><progress class='progressBar' id='pro' value='' max=''></progress><span class='percentage' id='per'></span></div>`;$("body").append(domStr);// FormData 对象var form = new FormData();form.append("author", "xueba");             // 可以增加表单数据for (let i = 0; i < fileObj.length; i++){form.append("file", fileObj[i]);        // 文件对象}// jQuery ajax$.ajax({url: "/file_upload/",type: "POST",async: true,      // 异步上传data: form,contentType: false, // 必须false才会自动加上正确的Content-TypeprocessData: false, // 必须false才会避开jQuery对 formdata 的默认处理。XMLHttpRequest会对 formdata 进行正确的处理xhr: function () {let xhr = $.ajaxSettings.xhr();xhr.upload.addEventListener("progress", progressSFunction, false);xhr.upload.onloadstart = (e) => {progress[0] = {last_laoded: 0,last_time: e.timeStamp,};console.log("开始上传",progress);};xhr.upload.onloadend = () => {delete progress[0];console.log("结束上传",progress);};return xhr;},success: function (data) {data = JSON.parse(data);data.forEach((i) => {console.log(i.code, i.file_url);});},error: function () {alert("aaa上传失败!");},});}// jQuery版本进度条function Progressbar(e) {var bar = $("#progressBar"); // 进度条var num = $("#percentage");  // 百分比if (e.lengthComputable) {bar.attr("max", e.total);bar.attr("value", e.loaded);num.text(Math.round(e.loaded / e.total * 100) + "%");}}// 原生js版 并行进度条function progressPFunction(e, k) {var progressBar = document.getElementById(`pro${k}`);var percentageDiv = document.getElementById(`per${k}`);if (e.lengthComputable) {progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[k].last_laoded) / (e.timeStamp - progress[k].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[k].last_laoded = e.loaded, progress[k].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;console.log(speed);}}// 原生js 串行进度条function progressSFunction(e) {var progressBar = document.getElementById(`pro`);var percentageDiv = document.getElementById(`per`);if (e.lengthComputable) // lengthComputable表示进度信息是否可用{progressBar.max = e.total;progressBar.value = e.loaded;let speed = (e.loaded - progress[0].last_laoded) / (e.timeStamp - progress[0].last_time) + " bytes/s";let percent = Math.round(e.loaded / e.total * 100) + "%";progress[0].last_laoded = e.loaded, progress[0].last_time = e.timeStamp;percentageDiv.innerHTML = percent + " " + speed;}}

Django后端

def file_upload(request):"ajax文件上传功能"resList, fileList = [], request.FILES.getlist("file")dir_path = 'static/files/{0}/{1}/{2}'.format(time.strftime("%Y"),time.strftime("%m"),time.strftime("%d"))if os.path.exists(dir_path) is False:os.makedirs(dir_path)for file in fileList:file_path = '%s/%s' % (dir_path, file.name)file_url = '/%s/%s' % (dir_path, file.name)res = {"code": 0, "file_url": ""}with open(file_path, 'wb') as f:if f == False:res['code'] = 1for chunk in file.chunks(): # chunks()代替read(),如果文件很大,可以保证不会拖慢系统内存f.write(chunk)res['file_url'] = file_urlresList.append(res)return HttpResponse(json.dumps(resList))

参考:

https://www.cnblogs.com/potatog/p/9342448.html

https://www.w3cmm.com/ajax/progress-events.html

转载于:https://www.cnblogs.com/V587Chinese/p/11371380.html

HTML文件上传与下载相关推荐

  1. Angular 文件上传与下载

    Angular文件上传与下载 文件上传 方式1 使用NG ZORRO中的组件. 文件下载 方式1 直接下载 方式2 通过HTTP请求后端数据的方式进行下载 文件上传 方式1 使用NG ZORRO中的组 ...

  2. SpringBoot下文件上传与下载的实现

    原文:http://blog.csdn.net/colton_null/article/details/76696674 SpringBoot后台如何实现文件上传下载? 最近做的一个项目涉及到文件上传 ...

  3. Python实现阿里云aliyun服务器里的文件上传与下载

    Python实现阿里云服务器里的文件上传与下载 Python实现阿里云服务器里的文件上传与下载 背景: 正文: 预备环境: 构想: 实现: 注意: 结尾 018.4.15 背景: 老实说,因为现实的各 ...

  4. java使用Jsch实现远程操作linux服务器进行文件上传、下载,删除和显示目录信息...

    1.java使用Jsch实现远程操作linux服务器进行文件上传.下载,删除和显示目录信息. 参考链接:https://www.cnblogs.com/longyg/archive/2012/06/2 ...

  5. php从ftp下载文件到本地,php使用ftp实现文件上传与下载功能

    本文实例为大家分享了php ftp文件上传与下载的具体代码,供大家参考,具体内容如下 ftp文件上传 php自带有ftp操作的函数包,一个比较简单实现的ftp文件上传操作可以通过以下几个步骤来完成: ...

  6. 2014-07-23 利用ASP.NET自带控件实现单文件上传与下载

    效果图 上传文件页面: 下载文件页面:   1.母版页site.Master <%@ Master Language="C#" AutoEventWireup="t ...

  7. PHP文件上传,下载,Sql工具类!

    PHP文件上传,下载,Sql工具类! 对文件大小,文件类型 同名覆盖 中文转码的操作,可直接使用 前台 upload.html <!DOCTYPE html> <html> & ...

  8. WSE3.0构建Web服务安全(4):MTOM消息传输优化和文件上传、下载

    MTOM消息优化传输机制主要应用于大量数据的传输,很多文章中也直接得出结论:使用MTOM文件传输效率高.为什么MTOM的数据传输效率会比别的方式要高?MTOM真的如此完美吗,它有什么不足?什么情况下使 ...

  9. [转载]ASP.NET Core文件上传与下载(多种上传方式)

    ASP.NET Core文件上传与下载(多种上传方式) 前言 前段时间项目上线,实在太忙,最近终于开始可以研究研究ASP.NET Core了. 打算写个系列,但是还没想好目录,今天先来一篇,后面在整理 ...

  10. 科普|不同协议下远程服务器文件上传_下载优劣对比

    作为一个程序员,如果不知道如何进行远程服务器的文件上传与下载,实在是一件尴尬的事情.打开百度,搜索「远程服务器 上传下载」,你能得到 63,100,000 个搜搜结果,五花八门的操作方式的让人眼花缭乱 ...

最新文章

  1. 优秀程序员必须知道的32个算法,提高你的开发效率
  2. Java环境创建_Java环境的搭建
  3. 十年肺腑之言:说说技术总监的“三板斧”
  4. Eclipse3.2安装简介
  5. C双拼输入法使用说明
  6. 【SCOI2009】粉刷匠
  7. echarts报表javascript插件简介
  8. 数据库期末大作业:机票预定信息系统数据库设计与实现
  9. 好家伙,查看系统日志时我捕获了一只发生概率小于万分之一的Bug
  10. 华擎主板bios设置图解_【华擎Z170评测】BIOS设置及超频方法简介_华擎 Z170 超频方程式_主板评测-中关村在线...
  11. webgl 绘制太阳 地球 月亮
  12. 5G PRB和RBG关系
  13. 华为还是赢了,高通为它定制新款芯片,率先在手机接入卫星通信
  14. PHP预设的配置模板,YzmCMS默认模板说明
  15. java2018年二级真题_2018年3月浙江省计算机二级(Java)综合能力测试题及答案解析【含真题】...
  16. 第十七届智能车越野硬件篇——无刷电机驱动
  17. 学大伟业:高二才开始学化学竞赛还来得及吗?
  18. 通过frp搭建属于自己的免费稳定的内网穿透服务
  19. 2011年各大知名IT公司校园招聘研发类薪资待遇收集
  20. 【英语语法入门】 第18讲 There-Here be 句型

热门文章

  1. 2020年第十八届西电程序设计竞赛网络预选赛之Problem D 由比滨结衣的饼干(二分+前缀后缀)
  2. Kuroni and Impossible Calculation CodeForces - 1305C(鸽巢原理)
  3. CCF之地铁修建(100分)
  4. 从二值检索到层次竞买图——让搜索广告关键词召回焕然新生
  5. 双11技术分享 | “喵糖”背后的商业化流量投放算法
  6. 没有run窗口_学会了面向对象,还怕没有对象?
  7. php redis 集合返回多条,详解PHP多个进程配合redis的有序集合实现大文件去重
  8. 深度学习之卷积神经网络(4)LeNet-5实战
  9. mysql inner join where_mysql中,inner join和where的结合问题
  10. 怎么让电脑屏幕一直亮着_笔记本开机白屏怎么回事 笔记本开机白屏解决方法【详解】...