整合营销服务商

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

免费咨询热线:

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



在前面

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

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

自测读不读

以下是本文所涉及到的知识点,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/


html转为pdf的组件有很多,但是还没有哪一款能达到这个效果,其只要原因是wkhtmltopdf使用webkit网页渲染引擎开发的用来将 html转成 pdf的工具,可以跟多种脚本语言进行集成来转换文档。但是就使用简便性来说还是itext等组件占据优势,如果你要转换格式有比较高的要求,那么wkhtmltopdf绝对是不二之选!

下载路径

官网地址 wkhtmltopdf.org/

github地址 github.com/wkhtmltopdf…

使用方法

  1. windows直接使用:只要在windows命令行中输入c:\wkhtmltopdf.exe my.oschina.net/papio/blog/… c:\blog.pdf 就可以把这篇文章转成pdf,并保存到C盘根目录。
  2. java调用:java中调用wkhtmltopdf的命令Runtime.getRuntime().exec("c:\wkhtmltopdf.exe my.oschina.net/papio/blog/… c:\blog.pdf")就可以实现转换。

java调用demo

public class HtmlToPdfInterceptor extends Thread { private InputStream is; public HtmlToPdfInterceptor(InputStream is){ this.is = is; } public void run(){ try{ InputStreamReader isr = new InputStreamReader(is, "utf-8"); BufferedReader br = new BufferedReader(isr); String line = null; while ((line = br.readLine()) != null) { System.out.println(line.toString()); //输出内容 } }catch (IOException e){ e.printStackTrace(); } }}public class HtmlToPdf { //wkhtmltopdf在系统中的路径 private static final String toPdfTool = "D:\wkhtmltopdf\bin\wkhtmltopdf.exe"; /** * html转pdf * @param srcPath html路径,可以是硬盘上的路径,也可以是网络路径 * @param destPath pdf保存路径 * @return 转换成功返回true */ public static boolean convert(String srcPath, String destPath){ File file = new File(destPath); File parent = file.getParentFile(); //如果pdf保存路径不存在,则创建路径 if(!parent.exists()){ parent.mkdirs(); } StringBuilder cmd = new StringBuilder(); cmd.append(toPdfTool); cmd.append(" "); cmd.append(" --header-line");//页眉下面的线 cmd.append(" --header-center 这里是页眉这里是页眉这里是页眉这里是页眉 ");//页眉中间内容 //cmd.append(" --margin-top 30mm ");//设置页面上边距 (default 10mm) cmd.append(" --header-spacing 10 ");//(设置页眉和内容的距离,默认0) cmd.append(srcPath); cmd.append(" "); cmd.append(destPath); boolean result = true; try{ Process proc = Runtime.getRuntime().exec(cmd.toString()); HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream()); HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream()); error.start(); output.start(); proc.waitFor(); }catch(Exception e){ result = false; e.printStackTrace(); } return result; } public static void main(String[] args) { HtmlToPdf.convert("https://my.oschina.net/papio/blog/835645", "d:/wkhtmltopdf.pdf"); }}复制代码

wkhtmltopdf 参数详解

wkhtmltopdf [OPTIONS]... <input file> [More input files] <output file>常规选项 --allow <path> 允许加载从指定的文件夹中的文件或文件(可重复) --book* 设置一会打印一本书的时候,通常设置的选项 --collate 打印多份副本时整理 --cookie <name> <value> 设置一个额外的cookie(可重复) --cookie-jar <path> 读取和写入的Cookie,并在提供的cookie jar文件 --copies <number> 复印打印成pdf文件数(默认为1) --cover* <url> 使用HTML文件作为封面。它会带页眉和页脚的TOC之前插入 --custom-header <name> <value> 设置一个附加的HTTP头(可重复) --debug-javascript 显示的javascript调试输出 --default-header* 添加一个缺省的头部,与页面的左边的名称,页面数到右边,例如: --header-left '[webpage]' --header-right '[page]/[toPage]' --header-line --disable-external-links* 禁止生成链接到远程网页 --disable-internal-links* 禁止使用本地链接 --disable-javascript 禁止让网页执行JavaScript --disable-pdf-compression* 禁止在PDF对象使用无损压缩 --disable-smart-shrinking* 禁止使用WebKit的智能战略收缩,使像素/ DPI比没有不变 --disallow-local-file-access 禁止允许转换的本地文件读取其他本地文件,除非explecitily允许用 --allow --dpi <dpi> 显式更改DPI(这对基于X11的系统没有任何影响) --enable-plugins 启用已安装的插件(如Flash --encoding <encoding> 设置默认的文字编码 --extended-help 显示更广泛的帮助,详细介绍了不常见的命令开关 --forms* 打开HTML表单字段转换为PDF表单域 --grayscale PDF格式将在灰阶产生 --help Display help --htmldoc 输出程序HTML帮助 --ignore-load-errors 忽略claimes加载过程中已经遇到了一个错误页面 --lowquality 产生低品质的PDF/ PS。有用缩小结果文档的空间 --manpage 输出程序手册页 --margin-bottom <unitreal> 设置页面下边距 (default 10mm) --margin-left <unitreal> 将左边页边距 (default 10mm) --margin-right <unitreal> 设置页面右边距 (default 10mm) --margin-top <unitreal> 设置页面上边距 (default 10mm) --minimum-font-size <int> 最小字体大小 (default 5) --no-background 不打印背景 --orientation <orientation> 设置方向为横向或纵向 --page-height <unitreal> 页面高度 (default unit millimeter) --page-offset* <offset> 设置起始页码 (default 1) --page-size <size> 设置纸张大小: A4, Letter, etc. --page-width <unitreal> 页面宽度 (default unit millimeter) --password <password> HTTP验证密码 --post <name> <value> Add an additional post field (repeatable) --post-file <name> <path> Post an aditional file (repeatable) --print-media-type* 使用的打印介质类型,而不是屏幕 --proxy <proxy> 使用代理 --quiet Be less verbose --read-args-from-stdin 读取标准输入的命令行参数 --readme 输出程序自述 --redirect-delay <msec> 等待几毫秒为JS-重定向(default 200) --replace* <name> <value> 替换名称,值的页眉和页脚(可重复) --stop-slow-scripts 停止运行缓慢的JavaScripts --title <text> 生成的PDF文件的标题(第一个文档的标题使用,如果没有指定) --toc* 插入的内容的表中的文件的开头 --use-xserver* 使用X服务器(一些插件和其他的东西没有X11可能无法正常工作) --user-style-sheet <url> 指定用户的样式表,加载在每一页中 --username <username> HTTP认证的用户名 --version 输出版本信息退出 --zoom <float> 使用这个缩放因子 (default 1) 页眉和页脚选项--header-center* <text> (设置在中心位置的页眉内容) --header-font-name* <name> (default Arial) (设置页眉的字体名称)--header-font-size* <size> (设置页眉的字体大小)--header-html* <url> (添加一个HTML页眉,后面是网址)--header-left* <text> (左对齐的页眉文本)--header-line* (显示一条线在页眉下)--header-right* <text> (右对齐页眉文本)--header-spacing* <real> (设置页眉和内容的距离,默认0)--footer-center* <text> (设置在中心位置的页脚内容) --footer-font-name* <name> (设置页脚的字体名称) --footer-font-size* <size> (设置页脚的字体大小default 11)--footer-html* <url> (添加一个HTML页脚,后面是网址)--footer-left* <text> (左对齐的页脚文本)--footer-line* 显示一条线在页脚内容上)--footer-right* <text> (右对齐页脚文本)--footer-spacing* <real> (设置页脚和内容的距离)./wkhtmltopdf --footer-right '[page]/[topage]' http://www.baidu.com baidu.pdf./wkhtmltopdf --header-center '报表' --header-line --margin-top 2cm --header-line http://192.168.212.139/oma/ oma.pdf表内容选项中 --toc-depth* <level> Set the depth of the toc (default 3) --toc-disable-back-links* Do not link from section header to toc --toc-disable-links* Do not link from toc to sections --toc-font-name* <name> Set the font used for the toc (default Arial) --toc-header-font-name* <name> The font of the toc header (if unset use --toc-font-name) --toc-header-font-size* <size> The font size of the toc header (default 15) --toc-header-text* <text> The header text of the toc (default Table Of Contents) --toc-l1-font-size* <size> Set the font size on level 1 of the toc (default 12) --toc-l1-indentation* <num> Set indentation on level 1 of the toc (default 0) --toc-l2-font-size* <size> Set the font size on level 2 of the toc (default 10) --toc-l2-indentation* <num> Set indentation on level 2 of the toc (default 20) --toc-l3-font-size* <size> Set the font size on level 3 of the toc (default 8) --toc-l3-indentation* <num> Set indentation on level 3 of the toc (default 40) --toc-l4-font-size* <size> Set the font size on level 4 of the toc (default 6) --toc-l4-indentation* <num> Set indentation on level 4 of the toc (default 60) --toc-l5-font-size* <size> Set the font size on level 5 of the toc (default 4) --toc-l5-indentation* <num> Set indentation on level 5 of the toc (default 80) --toc-l6-font-size* <size> Set the font size on level 6 of the toc (default 2) --toc-l6-indentation* <num> Set indentation on level 6 of the toc (default 100) --toc-l7-font-size* <size> Set the font size on level 7 of the toc (default 0) --toc-l7-indentation* <num> Set indentation on level 7 of the toc (default 120) --toc-no-dots* Do not use dots, in the toc轮廓选项 --dump-outline <file> 转储目录到一个文件 --outline 显示目录(文章中h1,h2来定) --outline-depth <level> 设置目录的深度(默认为4)页脚和页眉 * [page] 由当前正在打印的页的数目代替 * [frompage] 由要打印的第一页的数量取代 * [topage] 由最后一页要打印的数量取代 * [webpage] 通过正在打印的页面的URL替换 * [section] 由当前节的名称替换 * [subsection] 由当前小节的名称替换 * [date] 由当前日期系统的本地格式取代 * [time] 由当前时间,系统的本地格式取代
作者:曹元
链接:https://juejin.im/post/6856547881873047559
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

经常在一些报告业务场景,客户常常要求可以实现导出html、pdf、xmind格式的报告文件,生成pdf格式文件,网络上有很多开源的解决方案,但是生成html格式开源免费的解决方案很少,下面我给我大家介绍一下,如何解决这些问题

pdf

  • iText,生成PDF文档,还支持将XML、Html文件转化为PDF文件;
  • Apache PDFBox,生成、合并PDF文档;
  • docx4j,生成docx、pptx、xlsx文档,支持转换为PDF格式。

这些框架都支持,这里不再详细介绍,具体用户可以在github上,进行查阅

html

可以用一些收费的框架,可以做到,比如Aspose,但是在这里介绍一个思路,去解决这个问题,大家肯定对导出excel表格很熟悉把,填充数据与表格,然后还可以设置标头样式与单元格样式,那么我们可以按照这个思路,进行开发一个简易版本的导出html文件。

  • 代码逻辑
@Test
    void listToHtmlTableTest() throws IOException {
        List<String> headers = List.of("Name", "Age", "City");
        List<List<String>> data = List.of(
                List.of("gz", "25", "beijing"),
                List.of("jelly", "30", "he ze")
        );
        //头样式
        String headStyle = "border: 1px solid #000000;";
        //表格样式
        String bodyStyle="border: 1px solid #000000;";
        //转成html标签
        String htmlTable = HtmlTable.convertDataToHtml(headers, data,headStyle,bodyStyle);
        //输出html文件
        String fileName = "output.html";
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fileName))) {
            bufferedWriter.write(htmlTable);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

 public static String convertDataToHtml(List<String> headers, List<List<String>> data,String headStyle,String bodyStyle) {
        StringBuilder html = new StringBuilder();
        html.append("<table>");
        // table headers
        html.append("<thead><tr>");
        for (String header : headers) {
            html.append("<th style=\"").append(headStyle).append("\">").append(header).append("</th>");
        }
        html.append("</tr></thead>");

        // table body
        html.append("<tbody>");
        for (List<String> row : data) {
            html.append("<tr>");
            for (String cell : row) {
                html.append("<td style=\"").append(bodyStyle).append("\">").append(cell).append("</td>");
            }
            html.append("</tr>");
        }
        html.append("</tbody>");
        html.append("</table>");
        return html.toString();
    }代码运行效果
  • 代码运行效果

  • 优化点

可配置样式

导出封装

支持图片

支持模板导出

xmind

开源sdk:https://github.com/xmindltd/xmind-sdk-js

需要前端实现,因为官方没有提供java版本sdk

每天不断更,精彩不停止,明天见,我是行者

记得留个关注、点赞、评论哟,让我们一起去看星辰大海,品味代码人生