HTML文件上传与下载

文件下载

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

  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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247125.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NSURLSession的应用

iOS7以后发布了NSURLSession用来替换NSURLConnection&#xff0c;NSURLSession使用方式有以下两种&#xff1a; 1.block方式 &#xff08;1&#xff09;创建的步骤 获取单例会话对象 创建URL对象 隐含创建request 创建NSURLSessionDataTask // 1.获取会话对象 NSURLSess…

ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)...

在正式进入主题之前我们来看下几个概念&#xff1a; 一、依赖倒置 依赖倒置是编程五大原则之一&#xff0c;即&#xff1a; 1、上层模块不应该依赖于下层模块&#xff0c;它们共同依赖于一个抽象。 2、抽象不能依赖于具体&#xff0c;具体依赖于抽象。 其中上层就是指使用者&am…

iOS中XML解析

iOS中XML解析分为两种实现方式&#xff1a;SAX与DOM SAX方式&#xff1a;主要是事件驱动的解析方式&#xff0c;是逐行读取XML数据&#xff0c;不断回调代理&#xff0c;告诉代理当前解析的元素开始或者结束。 DOM解析方式&#xff1a;是讲整个XML数据全部读入内存&#xff0…

苹果电脑基本设置+Linux 命令+Android 实战集锦

本文微信公众号「AndroidTraveler」首发。 背景 大多数应届毕业生在大学期间使用的比较多的是 windows 电脑&#xff0c;因此初入职场如果拿到一台苹果电脑&#xff0c;可能一时间不能够很快的上手。基于此&#xff0c;这边出了系列视频&#xff0c;通过实际的演示让没使用过苹…

iOS中POST请求

iOS中POST请求的发送需要使用NSMutableURLRequest可以设置URL request的头字段&#xff0c;比如超时时间&#xff0c;请求类型&#xff1a;GET POST等一些关键头字段&#xff1a; - (IBAction)login { // 1.用户名 NSString *usernameText self.username.text; if (userna…

发送JSON数据给服务器

需要将JSON格式的数据传送给服务器&#xff0c;注意需要设置&#xff1a; [request setValue:”application/json” forHTTPHeaderField:”Content-Type”]; Content-Type类型为&#xff1a;application/json // 1.URL NSURL *url [NSURL URLWithString:"http://localh…

Mac中AndroidStudio没有找到Plugins的问题

我们在windows中都可以正常找到plugins 但是在Mac上AndroidStudio里 setting打开却没有plugins 正准备在Mac上搞一下flutter呢 我感觉智商受到了侮辱&#xff01; 这里其实是mac版本给我开了个玩笑 你可以按快捷键&#xff0c;你就可以找到 快捷键 command ‘,’ 没错就是comm…

进程和操作系统概述

进程和操作系统概述 进程的基础 程序和进程&#xff1a; 程序是一对静态的代码文件 进程是一个正在运行着的程序&#xff0c;抽象概念 进程由操作系统操控调用交于CPU运行 操作系统 1.管理控制协调计算机硬件和软件的关系 2.操作系统的作用&#xff1f; ​ 第一个作用&#xff…

iOS手势操作简介(一)

iOS中能够响应手势操作的类必须要继承自UIResponder&#xff0c;才能够处理手势响应操作。 默认继承了UIResponder的类有&#xff1a;UIApplication UIViewController UIView都继承自UIResponder. UIView是UIResponder的子类&#xff0c;可以实现下列4个方法处理不同的触摸事…

iOS开发中手势处理简介(二)

iOS中手势操作事件的产生于传递 发生触摸事件后&#xff0c;系统会将该事件加入到一个由UIApplication管理的事件队列中 UIApplication会从事件队列中取出最前面的事件&#xff0c;并将事件分发下去以便处理&#xff0c;通常&#xff0c;先发送事件给应用程序的主窗口&#x…

对前端Jenkins自动化部署的研究

1. 安装 安装 Nginx 1.1去官网下直接下载&#xff0c;解压缩 start nginx就可以使了&#xff0c;常用命令&#xff1a; start nginx # 启动 nginx -s reload # 修改配置后重新加载生效 nginx -s reopen # 重新打开日志文件 nginx -t # 配置文件检测是否正确 1.2 安装Jenkins…

python超神之路:Python3 列表list合并的4种方法

Python3 列表list合并的4种方法 方法1: 直接使用""号合并列表 aList [1,2,3] bList [www, pythontab.com] cList aList bList dList bList aList print(cList) print(dList) # 结果&#xff1a; [1, 2, 3, www, pythontab.com] [www, pythontab.com, 1, 2, 3] …

iOS手势操作简介(三)

监听触摸事件的做法 如果想监听一个view上面的触摸事件&#xff0c;之前的做法是 自定义一个view 实现view的touches方法&#xff0c;在方法内部实现具体处理代码 通过touches方法监听view触摸事件&#xff0c;有很明显的几个缺点 必须得自定义view 由于是在view内部的to…

iOS手势操作简介(四)

当事件传递到相应的UIResponder后&#xff0c;会首先调用&#xff1a; hitTest:withEvent: return (UIView *) UIApplication -> UIWindow 什么时候调用&#xff1a;当事件传递给一个控件的时候就会调用 作用&#xff1a;找最合适的viewhitTest:withEvent: return (UIView…

ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)...

在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入&#xff0c;本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入。 PS&#xff1a;本章将主要采用构造函数注入的方式&#xff0c;下一章将继续分享如何使之能够同…

iOS手势操作简介(五)

利用手势操作实现抽屉效果&#xff1a; 第一步&#xff1a;搭建UI (void)addChildView { // left UIView *leftView [[UIView alloc] initWithFrame:self.view.bounds]; leftView.backgroundColor [UIColor greenColor]; [self.view addSubview:leftView]; _leftView…

Java过滤器与SpringMVC拦截器之间的关系与区别

今天学习和认识了一下&#xff0c;过滤器和SpringMVC的拦截器的区别&#xff0c;学到了不少的东西&#xff0c;以前一直以为拦截器就是过滤器实现的&#xff0c;现在想想还真是一种错误啊&#xff0c;而且看的比较粗浅&#xff0c;没有一个全局而又细致的认识&#xff0c;由于已…

iOS手势操作简介(六)

利用UIGestureRecognizer来对手势进行处理&#xff1a; interface HMViewController () property (weak, nonatomic) IBOutlet UIImageView *imagView; end implementation HMViewController (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup aft…

iOS并行程序开发- GCD NSOperationQueue(1)

import UIKit let imageURLs [“http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg“, “http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg“, “http://algoos.com/wp-content/uploads/2015/08/ireland-02.jpg“, “http:…

二次幂权限设计

设置含有的权限如增删改查减为1,2,4,8,16 如果A包含增删改这5个权限&#xff0c;那A的值为1247 如果B包含增改查这5个权限&#xff0c;那A的值为14813 如果C包含增删改查减这5个权限&#xff0c;那A的值为12481631 7二进制为111,13的二进制为1101,31二进制为11111 1二进制为1&a…