WEB:文件上传 您所在的位置:网站首页 上传附件是指图片吗还是文件 WEB:文件上传

WEB:文件上传

2024-05-25 21:55| 来源: 网络整理| 查看: 265

文件上传知识体系

如下图

1. 协议规范(RFC 1867)

HTML 表单最初只支持 application/x-www-form-urlencoded 形式编码(key=value&key=value...),但它不适合用于传输二进制数据(文件)或者包含非ASCII字符的数据。所以 multipart/form-data 就诞生了,专门用于传输文件。

HTML 的二进制文件传输特性,最初在《RFC 1867:Form-based File Upload in HTML》中定义。 扩充 type="file" 类型, 用于实现文件选择; 扩充 enctype="multipart/form-data" 编码形式,用于支持文件传输;代码语言:javascript复制 文本域: 单文件: 多文件: 没文件: 提交

2. 文件上传请求响应

2.1. Servlet 3.x(MultipartConfig)

Servlet 3.x 大法好,无需插件,就能处理上传的文件。

代码语言:javascript复制import org.apache.commons.io.IOUtils; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; import java.nio.charset.Charset; import java.util.Collection; import java.util.Iterator; @MultipartConfig( fileSizeThreshold = 512, // 超过这个值,就暂存到磁盘 location = "d:/temp", // 暂存区 maxFileSize = 1024, // 请求中单个文件的最大尺寸 maxRequestSize = 2048 // 请求的最大尺寸 ) @WebServlet(name = "RecvFile", urlPatterns = {"/recvfile"}) public class RecvFile extends javax.servlet.http.HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Collection parts = request.getParts(); Iterator itor = parts.iterator(); while (itor.hasNext()) { Part part = itor.next(); String fieldName = part.getName(); // 字段名 String fileName = part.getSubmittedFileName(); // 文件名(注:此接口Servlet 3.1才有) String fileType = part.getContentType(); String fileContent = new String( IOUtils.toByteArray( part.getInputStream() ), Charset.forName("UTF-8") ); System.out.format( "field:%s, fileName:%s, type:%s, content:%s\n", fieldName, fileName, fileType, fileContent); } } }代码语言:javascript复制field:textfield, fileName:null, type:null, content:琦玉 field:single, fileName:杰洛斯.txt, type:text/plain, content:谢谢您为我解惑,老师的战斗为我指明了道路,强大的象征,终极目标的所在,我也要到那里去。 field:multi, fileName:琦玉1.txt, type:text/plain, content:没有什么是一拳解决不了的,如果有,那就两拳。 field:multi, fileName:琦玉2.txt, type:text/plain, content:咱回吧。 field:empty, fileName:, type:application/octet-stream, content:

2.2. Apache Commons Upload

Servlet 2.x 环境自身无法方便的处理文件上传请求,第三方工具 Apache Commons Upload 则是最好的选择。

关键依赖:

代码语言:javascript复制 org.apache.commons commons-lang3 3.9 commons-io commons-io 2.6 commons-fileupload commons-fileupload 1.4

代码示例:

代码语言:javascript复制import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.io.IOUtils; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; @WebServlet(name = "ApacheRecvFile", urlPatterns = {"/apacherecvfile"}) public class ApacheRecvFile extends javax.servlet.http.HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 检测是否为文件上传请求 if (!ServletFileUpload.isMultipartContent(request)) { throw new RuntimeException("不是文件上传请求!"); } // 配置上传文件缓存策略 // 1. SizeThreshold: 缓存文件大小阈值 // a.上传文件小于此阈值,暂存于内存; // b.上传文件大于此阈值,暂存于磁盘; // 2. Repository: 上传文件暂存于磁盘时的目录; DiskFileItemFactory factory = new DiskFileItemFactory(); factory.setSizeThreshold(512); // 默认值:10240 factory.setRepository(new File("d:/temp")); // 文件上传相关参数 // 1. FileSizeMax: 限制请求中单个文件大小 // 2. SizeMax: 限制请求的总大小 ServletFileUpload upload = new ServletFileUpload(factory); upload.setFileSizeMax(1024); // 默认:-1,无限制 upload.setSizeMax(2048); // 默认:-1,无限制 // 分析请求 List items = null; try { items = upload.parseRequest(request); } catch (FileUploadException e) { throw new RuntimeException(e); } Iterator iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); if (item.isFormField()) { // 普通字段 String fieldName = item.getFieldName(); //字段名 String fileContent = item.getString("UTF-8");//字段值 System.out.format( "field:%s, content:%s\n", fieldName, fileContent); } else { // 文件字段 String fieldName = item.getFieldName(); // 字段名 String fileName = item.getName(); // 文件名 String fileType = item.getContentType(); String fileContent = new String( IOUtils.toByteArray( item.getInputStream() ), Charset.forName("UTF-8") ); System.out.format( "field:%s, fileName:%s, type:%s, content:%s\n", fieldName, fileName, fileType, fileContent); } } } }代码语言:javascript复制field:textfield, content:琦玉 field:single, fileName:D:\杰洛斯.txt, type:text/plain, content:谢谢您为我解惑,老师的战斗为我指明了道路,强大的象征,终极目标的所在,我也要到那里去。 field:multi, fileName:D:\\琦玉1.txt, type:text/plain, content:没有什么是一拳解决不了的,如果有,那就两拳。 field:multi, fileName:D:\\琦玉2.txt, type:text/plain, content:咱回吧。 field:empty, fileName:, type:application/octet-stream, content:

2.3. Spring MVC

Spring MVC 是一个分层的 Java Web 开发框架。Spring MVC的核心元素就是 Dispatcher Servlet,负责处理所有请求,但 DispatcherServlet 并没有实现任何解析 multipart 请求数据的功能。它将该任务委托给了Spring 中 MultipartResolver 策略接口的实现,通过这个实现类来解析 multipart 请求中的内容。Spring内置了两个 MultipartResolver 的实现供:

CommonsMultipartResolver:依赖 Apache Commons Upload 解析 multipart 请求。StandardServletMultipartResolver:依赖 Servlet 3.x 对 multipart 请求的原生支持。

2.3.1 CommonsMultipartResolver 示例

关键依赖:

代码语言:javascript复制 org.springframework spring-webmvc 5.1.8.RELEASE org.apache.commons commons-lang3 3.9 commons-io commons-io 2.6 commons-fileupload commons-fileupload 1.4

代码示例:web.xml

代码语言:javascript复制 xupload xupload org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml 1 xupload /mvc/* index.jsp

代码示例:springmvc.xml

代码语言:javascript复制

代码示例:SpringMVCApacheCommonsUploadController

代码语言:javascript复制package webj2ee; import org.apache.commons.io.IOUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.Charset; import java.util.Iterator; import java.util.List; @RestController public class SpringMVCApacheCommonsUploadController { @RequestMapping(value = "/recvfile") public void handleUpload(HttpServletRequest request) throws IOException { MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; Iterator fileNames = multipartRequest.getFileNames(); while (fileNames.hasNext()) { String fieldName = fileNames.next(); List files = multipartRequest.getFiles(fieldName); for (MultipartFile file : files) { String fileName = file.getOriginalFilename(); String fileType = file.getContentType(); String fileContent = new String( IOUtils.toByteArray( file.getInputStream() ), Charset.forName("UTF-8") ); System.out.format( "field:%s, fileName:%s, type:%s, content:%s\n", fieldName, fileName, fileType, fileContent); } } } }

2.3.2 StandardServletMultipartResolver 示例

与 CommonsMultipartResolver 示例相比,

只有 web.xml、springmvc.xml 略有差异;

代码示例:web.xml

代码语言:javascript复制 xupload xupload org.springframework.web.servlet.DispatcherServlet contextConfigLocation classpath:springmvc.xml 1 d:/temp 1024 2048 512 xupload /mvc/* index.jsp

代码示例:springmvc.xml

代码语言:javascript复制

3. 文件上传请求发起

3.1. Server 端发起(HttpClient)

应用场景:Server 端请求转发;

关键依赖:

代码语言:javascript复制 org.apache.httpcomponents httpclient 4.5.9 org.apache.httpcomponents httpmime 4.5.9

代码示例:

代码语言:javascript复制package webj2ee; import org.apache.http.HttpEntity; import org.apache.http.clienthods.CloseableHttpResponse; import org.apache.http.clienthods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; public class SendMultiPartRequest { public static void main(String[] args) { // 构造连接池 PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(); cm.setMaxTotal(4); cm.setDefaultMaxPerRoute(2); // CloseableHttpClient CloseableHttpClient httpclient = HttpClients.custom().setConnectionManager(cm).build(); // 构造 MultiPart 请求实体 MultipartEntityBuilder mpEntityBuilder = MultipartEntityBuilder.create(); mpEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); // 注意字符集 mpEntityBuilder.setCharset(Charset.forName("UTF-8")); mpEntityBuilder.addTextBody("text", "这是普通文本字段", ContentType.TEXT_PLAIN.withCharset("UTF-8")); // MultiPart 普通字段 mpEntityBuilder.addBinaryBody("file1", new File("D:/杰洛斯.txt")); // Mulitpart 文件字段 mpEntityBuilder.addBinaryBody("file2", new File("D:/琦玉1.txt")); HttpEntity entity = mpEntityBuilder.build(); // 构造 POST 请求 HttpPost httpPost = new HttpPost("http://localhost:8080/xupload/apacherecvfile"); httpPost.setEntity(entity); // 发送请求 try { CloseableHttpResponse closeableResponse = httpclient.execute(httpPost); } catch (IOException e) { throw new RuntimeException(e); } } }

注意事项:注意设置字符集,小心乱码;

3.2. 客户端发起 —— Flash(Uploadify)

Flash(Uploadify)的唯一价值就是增强了 IE7、8、9 的文件上传能力。如果你没办法甩开IE 这个小垃圾(特别是低版本IE),而且还想实现丰富的文件上传功能,Flash(Uploadify)是你唯一的选择。

优点:

a. √ 兼容IE7、IE8、IE9 b. √ 支持上传完成回调机制; c. √ 支持多选文件上传; d. √ 支持筛选上传文件类型; e. √ 支持限定上传文件尺寸; f. √ 支持文件上传进度监控;

缺点:

a. 要求客户端安装 Flash 控件; b. Cookie 在 Safari 环境下不能正常发送;

图:官方对 Session Cookie 问题的说明

代码示例:

代码语言:javascript复制 $("#myMultiFileUpload").uploadify({ swf : './uploadify/uploadify.swf', // 指明flash插件路径 uploader : './apacherecvfile', // 文件上传请求地址 width : 120, height : 30, fileObjName : "myfile", // 相当于 =IE10、Chrome、Firefox): a. √ 支持上传完成回调机制; b. √ 支持多选文件上传; c. √ 支持筛选上传文件类型; d. × 支持限定上传文件尺寸; e. × 支持文件上传进度监控;

缺点:低版本浏览器上,能力偏弱;

代码示例:form_upload_ie8_ie9.jsp

代码语言:javascript复制 function fileUploadSuccess(params){ alert("fileUploadSuccess called!\n"+params); }

‍代码示例:form_upload_callback_trigger.jsp

代码语言:javascript复制 submitted files info ${submittedFilesInfo} parent.${callbackFnName}("${callbackParams}");

代码示例:RecvFilesWithCallback.java

代码语言:javascript复制package webj2ee; import javax.servlet.ServletException; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.Part; import java.io.IOException; import java.util.Collection; import java.util.Iterator; @MultipartConfig() @WebServlet("/recvfileswithcallback") public class RecvFilesWithCallback extends HttpServlet { private static final long serialVersionUID = 1L; public RecvFilesWithCallback() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); // 解析上传文件 StringBuilder submittedFilesInfo = new StringBuilder(); Collection parts = request.getParts(); Iterator itor = parts.iterator(); while(itor.hasNext()) { Part part = itor.next(); String name = part.getName(); String submittedFileName = part.getSubmittedFileName(); long size = part.getSize(); String contentType = part.getContentType(); submittedFilesInfo.append(name+": "+submittedFileName+", "+contentType+", "+size/1000 + "K" + "\r\n"); } // 回调函数名、参数 String callbackFnName = request.getParameter("callbackFnName"); String callbackParams = "来自Servlet的返回值..."; // 返回上传成功页面,触发上传成功回调函数 request.setAttribute("submittedFilesInfo", submittedFilesInfo); request.setAttribute("callbackFnName", callbackFnName); request.setAttribute("callbackParams", callbackParams); request.getRequestDispatcher("./form_upload_callback_trigger.jsp").forward(request, response); } }

效果展示:

3.4. 客户端发起 —— FileAPI + XMLHttpRequest 上传

这是功能最强大、最灵活的文件上传方案。

优点:功能强大、灵活、定制性强

老古董(IE7/8/9): × 传统浏览器环境中,不支持 Ajax 文件上传; 现代浏览器(>=IE10、Chrome、Firefox): a. √ 支持上传完成回调机制; b. √ 支持多选文件上传; c. √ 支持筛选上传文件类型; d. √ 支持限定上传文件尺寸; e. √ 支持文件上传进度监控;

缺点:只能在现代浏览器环境中使用;

3.4.1 File API

H5 提供了一组简洁有效的文件操作接口:File API

主要涉及:

FileList:用户通过file控件或拖拽选择的一组文件; File:FileList里面放的就是File; Blob:代表一段二进制数据,File就是继承自Blob; FileReader:用于从File、Blob中读取数据; FormData:用Ajax实现上传、进度显示时会用到;

特别注意:

H5 的 File API 虽然可以让我们访问本地文件系统,但只能被动地读取,也就是说只有用户主动触发了文件读取行为(比如通过file控件选择选择文件或拖拽文件),才能访问到File API;

浏览器兼容性:

3.4.2 XMLHttpRequest Level 2

特别注意,是 XMLHttpRequest Level 2,支持文件上传、上传进度展示等特性。与 H5 的 File API 相结合,可以发挥很大

浏览器兼容性:

例1:获取用户选择的文件(FileList、File)

核心逻辑:

代码示例:

代码语言:javascript复制 $("#myfile").bind("change", function(e){ var fileList = this.files; console.dir(fileList); for(var i=0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有