api Form 表单上传附件python 前端form表单上传文件_java 上传大文件


文章目录

以下是本文所涉及到的知识点,break or continue ?

  • 文件上传原理
  • 最原始的文件上传
  • 使用 koa2 作为服务端写一个文件上传接口
  • 单文件上传和上传进度
  • 多文件上传和上传进度
  • 拖拽上传
  • 剪贴板上传
  • 大文件上传之分片上传
  • 大文件上传之断点续传
  • node 端文件上传

原理概述

原理很简单,就是根据 http 协议的规范和定义,完成请求消息体的封装和消息体的解析,然后将文件进行保存。

我们都知道如果要上传一个文件,需要把 form 标签的enctype设置为multipart/form-data,同时method必须为post方法。

那么multipart/form-data表示什么呢?

multipart互联网上的混合资源,就是资源由多种元素组成,form-data表示可以使用HTML Forms 和 POST 方法上传文件,具体的定义可以参考RFC 7578。

multipart/form-data 结构

看下 http 请求的消息体


api Form 表单上传附件python 前端form表单上传文件_java 大文件上传_02


  • 请求头:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次请求要上传文件,其中boundary表示分隔符,如果要上传多个表单项,就要使用boundary分割,每个表单项由———XXX开始,以———XXX结尾。

  • 消息体- Form Data 部分

每一个表单项又由Content-Type和Content-Disposition组成。

Content-Disposition: form-data 为固定值,表示一个表单元素,name 表示表单元素的 名称,回车换行后面就是name的值,如果是上传文件就是文件的二进制内容。

Content-Type:表示当前的内容的 MIME 类型,是图片还是文本还是二进制数据。

解析

客户端发送请求到服务器后,服务器会收到请求的消息体,然后对消息体进行解析,解析出哪是普通表单哪些是附件。

可能大家马上能想到通过正则或者字符串处理分割出内容,不过这样是行不通的,二进制buffer转化为string,对字符串进行截取后,其索引和字符串是不一致的,所以结果就不会正确,除非上传的就是字符串。

不过一般情况下不需要自行解析,目前已经有很成熟的三方库可以使用。

至于如何解析,这个也会占用很大篇幅,后面的文章在详细说。

最原始的文件上传

使用 form 表单上传文件

在 ie时代,如果实现一个无刷新的文件上传那可是费老劲了,大部分都是用 iframe 来实现局部刷新或者使用 flash 插件来搞定,在那个时代 ie 就是最好用的浏览器(别无选择)。

DEMO


api Form 表单上传附件python 前端form表单上传文件_java 大文件上传_03


这种方式上传文件,不需要 js ,而且没有兼容问题,所有浏览器都支持,就是体验很差,导致页面刷新,页面其他数据丢失。

HTML

选择文件: input 必须设置 name 属性,否则数据无法发送

标题:上 传

文件上传接口

服务端文件的保存基于现有的库koa-body结合 koa2实现服务端文件的保存和数据的返回。

在项目开发中,文件上传本身和业务无关,代码基本上都可通用。

在这里我们使用koa-body库来实现解析和文件的保存。

koa-body 会自动保存文件到系统临时目录下,也可以指定保存的文件路径。


api Form 表单上传附件python 前端form表单上传文件_ie6多文件上传_04


然后在后续中间件内得到已保存的文件的信息,再做二次处理。

  • ctx.request.files.f1 得到文件信息,f1为input file 标签的 name
  • 获得文件的扩展名,重命名文件

NODE

/** * 服务入口 */var http = require('http');var koaStatic = require('koa-static');var path = require('path');var koaBody = require('koa-body');//文件保存库var fs = require('fs');var Koa = require('koa2');var app = new Koa();var port = process.env.PORT || '8100';var uploadHost= `http://localhost:${port}/uploads/`;app.use(koaBody({ formidable: { //设置文件的默认保存目录,不设置则保存在系统临时目录下 os uploadDir: path.resolve(__dirname, '../static/uploads') }, multipart: true // 开启文件上传,默认是关闭}));//开启静态文件访问app.use(koaStatic( path.resolve(__dirname, '../static') ));//文件二次处理,修改名称app.use((ctx) => { var file = ctx.request.files.f1;//得道文件对象 var path = file.path; var fname = file.name;//原文件名称 var nextPath = path+fname; if(file.size>0 && path){ //得到扩展名 var extArr = fname.split('.'); var ext = extArr[extArr.length-1]; var nextPath = path+'.'+ext; //重命名文件 fs.renameSync(path, nextPath); } //以 json 形式输出上传文件地址 ctx.body = `{ "fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}" }`;});/** * http server */var server = http.createServer(app.callback());server.listen(port);console.log('demo1 server start ...... ');

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo1

多文件上传

在 ie 时代的多文件上传是需要创建多个 input file 标签,现在 html5只需要一个标签加个属性就搞定了,file 标签开启multiple。

DEMO


api Form 表单上传附件python 前端form表单上传文件_ie6多文件上传_05


HTML

//设置 multiple属性

NODE

服务端也需要进行简单的调整,由单文件对象变为多文件数组,然后进行遍历处理。

//二次处理文件,修改名称app.use((ctx) => {  var files = ctx.request.files.f1;// 多文件, 得到上传文件的数组 var result=[]; //遍历处理 files && files.forEach(item=>{ var path = item.path; var fname = item.name;//原文件名称 var nextPath = path + fname; if (item.size > 0 && path) { //得到扩展名 var extArr = fname.split('.'); var ext = extArr[extArr.length - 1]; var nextPath = path + '.' + ext; //重命名文件 fs.renameSync(path, nextPath);  //文件可访问路径放入数组 result.push(uploadHost+ nextPath.slice(nextPath.lastIndexOf('/') + 1)); } });  //输出 json 结果 ctx.body = `{ "fileUrl":${JSON.stringify(result)} }`;})

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo2

局部刷新 - iframe

这里说的是在 ie 时代的上传文件局部刷新,借助 iframe 实现。

DEMO


api Form 表单上传附件python 前端form表单上传文件_ie6多文件上传_06


  • 局部刷新

页面内放一个隐藏的 iframe,或者使用 js 动态创建,指定 form 表单的 target 属性值为iframe标签 的 name 属性值,这样 form 表单的 shubmit 行为的跳转就会在 iframe 内完成,整体页面不会刷新。

  • 拿到接口数据

然后为 iframe 添加load事件,得到 iframe 的页面内容,将结果转换为 JSON 对象,这样就拿到了接口的数据

HTML

选择文件(可多选):

input 必须设置 name 属性,否则数据无法发送

标题:上 传

NODE

服务端代码不需要改动,略.

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo3

无刷新上传

无刷新上传文件肯定要用到XMLHttpRequest,在 ie 时代也有这个对象,单只 支持文本数据的传输,无法用来读取和上传二进制数据。

现在已然升级到了XMLHttpRequest2,较1版本有非常大的升级,首先就是可以读取和上传二进制数据,可以使用·FormData·对象管理表单数据。

当然也可使用 fetch 进行上传。

DEMO


api Form 表单上传附件python 前端form表单上传文件_java 大文件上传_07


HTML


选择文件(可多选): 上 传


JS xhr

JS Fetch

fetch('http://localhost:8100/', { method: 'POST', body: fd }) .then(response => response.json()) .then(response =>{ console.log(response); if (response.fileUrl.length) { alert('上传成功'); } } ) .catch(error => console.error('Error:', error));

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo4

多文件,单进度

借助XMLHttpRequest2的能力,实现多个文件或者一个文件的上传进度条的显示。

DEMO


api Form 表单上传附件python 前端form表单上传文件_form表单上传文件_08


说明

  • 页面内增加一个用于显示进度的标签 div.progress
  • js 内处理增加进度处理的监听函数xhr.upload.onprogress
  • event.lengthComputable这是一个状态,表示发送的长度有了变化,可计算
  • event.loaded表示发送了多少字节
  • event.total表示文件总大小
  • 根据event.loaded和event.total计算进度,渲染div.progress

PS

xhr.upload.onprogress要写在xhr.send方法前面,否则event.lengthComputable状态不会改变,只有在最后一次才能获得,也就是100%的时候.

HTML


选择文件(可多选): 上 传


JS

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo5

多文件上传+预览+取消

上一个栗子的多文件上传只有一个进度条,有些需求可能会不大一样,需要观察到每个文件的上传进度,并且可以终止上传。

DEMO


api Form 表单上传附件python 前端form表单上传文件_java 上传大文件_09


说明

  • 为了预览的需要,我们这里选择上传图片文件,其他类型的也一样,只是预览不方便
  • 页面内增加一个多图预览的容器div.img-box
  • 根据选择的文件信息动态创建所属的预览区域和进度条以及取消按钮
  • 为取消按钮绑定事件,调用xhr.abort();终止上传
  • 使用window.URL.createObjectURL预览图片,在图片加载成功后需要清除使用的内存window.URL.revokeObjectURL(this.src);

HTML


选择文件(可多选):

添加文件

上 传

JS

问题1

这里没有做上传的并发控制,可以通过控制同时可上传文件的个数(这里控制为最多6个)或者上传的时候做好并发处理,也就是同时只能上传 X 个文件。

问题2

在测试过程中,取消请求的方法xhr.abort()调用后,xhr.readyState会立即变为4,而不是0,所以这里需要做容错处理。

MDN 上说是0.


api Form 表单上传附件python 前端form表单上传文件_ie6多文件上传_10


如果大家有不同的结果,欢迎留言。

CODE

https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo/demo11

拖拽上传

html5的出现,让拖拽上传交互成为可能,现在这样的体验也屡见不鲜。

DEMO


api Form 表单上传附件python 前端form表单上传文件_form表单上传文件_11


说明

  • 定义一个允许拖放文件的区域div.drop-box
  • 取消drop 事件的默认行为e.preventDefault();,不然浏览器会直接打开文件
  • 为拖拽区域绑定事件,鼠标在拖拽区域上 dragover, 鼠标离开拖拽区域dragleave, 在拖拽区域上释放文件drop
  • drop事件内获得文件信息e.dataTransfer.files