整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

手把手教你前端的各种文件上传攻略和大文件断点续传



在前面

今年国庆假期终于可以憋在家里了不用出门了,不用出去看后脑了,真的是一种享受。这么好的光阴怎么浪费,睡觉、吃饭、打豆豆这怎么可能(耍多了也烦),完全不符合我们程序员的作风,赶紧起来把文章写完。

这篇文章比较基础,在国庆期间的业余时间写的,这几天又完善了下,力求把更多的前端所涉及到的关于文件上传的各种场景和应用都涵盖了,若有疏漏和问题还请留言斧正和补充。

自测读不读

以下是本文所涉及到的知识点,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 请求的消息体



  • 请求头:

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



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

HTML

 <form method="post" action="http://localhost:8100" enctype="multipart/form-data">

        选择文件:
            <input type="file" name="f1"/> input 必须设置 name 属性,否则数据无法发送<br/>
<br/>
            标题:<input type="text" name="title"/><br/><br/><br/>

        <button type="submit" id="btn-0">上 传</button>

</form>

复制代码

文件上传接口

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

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

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

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



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

  • 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/

开浏览器从输入网址到网页呈现在大家面前,背后到底发生了什么?经历怎么样的一个过程?

先给大家来张总体流程图,具体步骤请看下文分解!

从URL输入到页面展现

一、URL到底是啥

URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址。比如: http://www.w3school.com.cn/html/index.asp,遵守以下的语法规则scheme://host.domain:port/path/filename

各部分解释如下:

  • scheme – 定义因特网服务的类型。常见的协议有http、https、ftp、file,其中最常见的类型是 http,而https则是进行加密的网络传输。
  • host – 定义域主机(http 的默认主机是 www)。
  • domain – 定义因特网域名,比如 w3school.com.cn。
  • port – 定义主机上的端口号(http 的默认端口号是 80)。
  • path – 定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)。
  • filename – 定义文档/资源的名称。

二、域名解析(DNS)

在浏览器输入网址后,首先要经过域名解析,因为浏览器并不能识别域名,需要通过域名直接找到相应的IP地址。大家这里或许会有个疑问——为啥要设置域名?怎么不一开始就给个IP地址?这样可以省去解析麻烦。

我们先来了解下什么是IP地址?

1. IP地址

IP地址是指互联网协议地址,是IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。IP地址是一个32位的二进制数,比如:127.0.0.1为本机IP,如果每个网址都是一串数字,那就不便于记忆!

域名就相当于IP地址乔装打扮的伪装者,带着一副面具,它的作用就是便于记忆和沟通的一组服务器的地址。

但这样有时候会带来一种风险——DNS劫持,就是使域名对应的不再是原本对应的IP,其效果就是对特定的网络不能访问或访问的是假网址,又难于被用户发觉,曾导致巴西最大银行巴西银行近1%客户受到攻击而导致账户被盗。

2. 什么是域名解析

DNS是一个网络服务器,我们的域名解析简单来说就是在DNS上记录一条信息记录。例如:baidu.com 220.114.23.56(服务器外网IP地址)80(服务器端口号)。

浏览器通过向DNS服务器发送域名,DNS服务器查询到与域名相对应的IP地址,然后返回给浏览器,浏览器再将IP地址打在协议上,同时请求参数也会在协议搭载,然后一并发送给对应的服务器,下一步就到了服务器处理阶段的工作。

3. 域名解析流程

  • 浏览器缓存:如果在之前对该url指定的主机进行过访问,浏览器会缓存该主机的IP一段时间(该时间浏览器指定),然后通过该IP地址找到对应主机;
  • 系统缓存:若浏览器中无该缓存,那么就到系统缓存中进行查询,浏览器会进行系统调用,查询缓存;
  • 路由器缓存:如果系统缓存中也没有,那么就到路由器缓存中进行查询;
  • ISP DNS 缓存:如果路由器缓存依旧未命中,那么就到ISP DNS中查询,一般的域名都能在这里查询得到;
  • 递归搜索:如果以上都没有查询到,那么就会到顶级域名服务器的根服务器中进行递归查询,只要该域名存在就肯定能找得到。

三、服务器处理响应请求

1. 服务器

服务器是网络环境中的高性能计算机,它侦听网络上的其他计算机(客户机)提交的服务请求,并提供相应的服务。比如:网页服务、文件下载服务、邮件服务、视频服务。

而客户端主要的功能是浏览网页、看视频、听音乐等等,两者截然不同。 每台服务器上都会安装处理请求的应用——web server,常见的web server产品有apache、nginx、IIS或Lighttpd等。

web server 担任管控的角色,对于不同用户发送的请求,会结合配置文件,把不同请求委托给服务器上处理相应请求的程序进行处理(例如:CGI脚本,JSP脚本,servlets,ASP脚本,服务器端JavaScript,或者一些其它的服务器端技术等),然后返回后台程序处理产生的结果作为响应。

服务器和客户端区别

2. MVC后台处理阶段

后台开发现在有很多框架,但大部分都还是按照MVC设计模式进行搭建的。

MVC是一个设计模式,将应用程序分成三个核心部件:模型(model)——视图(view)——控制器(controller),它们各自处理自己的任务,实现输入、处理和输出的分离。

MVC架构

(1)视图(view)

它是提供给用户的操作界面,是程序的外壳。

(2)模型(model)

模型主要负责数据交互,在MVC的三个部件中,模型拥有最多的处理任务,一个模型能为多个视图提供数据。

(3)控制器(controller)

它负责根据用户从”视图层”输入的指令,选取”模型层”中的数据,然后对其进行相应的操作,产生最终结果。控制器属于管理者角色,从视图接收请求并决定调用哪个模型构件去处理请求,然后再确定用哪个视图来显示模型处理返回的数据。

这三层是紧密联系在一起的,但又是互相独立的,每一层内部的变化不影响其他层,每一层都对外提供接口(Interface),供上面一层调用。

至于这一阶段发生什么?

简而言之,首先浏览器发送过来的请求先经过控制器,控制器进行逻辑处理和请求分发,接着会调用模型。这一阶段模型会获取redis db以及MySQL的数据,获取数据后将渲染好的页面,通过视图返回给浏览器,最后浏览器通过渲染引擎将网页呈现在用户面前。因此,下一步就来到浏览器处理阶段

四、浏览器的处理

浏览器拿到响应文本HTML后,以chrome浏览器为例,介绍下浏览器渲染机制

chrome浏览器渲染机制:

  • 处理 HTML 标记并构建 DOM 树。
  • 处理 CSS 标记并构建 CSSOM 树。
  • 将 DOM 与 CSSOM 合并成一个渲染树(render 树)。
  • 根据渲染树来布局,以计算每个节点(也就是每个Element)的几何位置,这又叫layout和reflow过程。
  • 最后通过调用操作系统Native GUI的API绘制,将各个节点绘制到屏幕上。
  • 于是就来到了绘制网页的最后阶段。

五、绘制网页

浏览器根据html和css计算得到渲染树之后,将渲染好的页面图像显示出来,即绘制网页,并开始响应用户的操作。

六、后记

随着学习的深入,对于页面加载这个主题认识更加深刻,之前一些困惑点现在都迎刃而解。

作者:浪里行舟

原文链接:https://www.jianshu.com/p/40d76ebb94e2

本文由 @浪里行舟 授权发布于人人都是产品经理,未经作者许可,禁止转载

题图来自Unsplash,基于CCO协议

ngular 装饰器 HostListener 监听DOM事件,本例监听回车键(Enter)与换行按键(Ctrl Enter)事件处理发送消息示例。

一直用Angular前端框架做项目,UI借用第三方库,在处理过程中很少操作相关基础,更多是在业务层面上。

因此工作中遇到此类将其记录下来作为学习过程。

@HostListener() 装饰器

Decorator that declares a DOM event to listen for, and provides a handler method to run when that event occurs.

eventName: string The DOM event to listen for.

args: A set of arguments to pass to the handler method when the event occurs.

参考这里。

示例场景

聊天窗口发送消息按键,回车(Enter)发送消息,换行(Ctrl Enter)对其文本换行。

Angular HostListener 监听回车事件

使用方法

在当前所需组件中从@angular/core导入HostListener

显示图如上述场景,按回车(Enter)键发送消息,按Ctrl Enter键换行输入消息。

Angular 装饰器 HostListener 监听DOM事件

发送消息窗口示例图

import { Component, OnInit, HostListener, Input } from '@angular/core';