文件上传知识体系
如下图
1. 协议规范(RFC 1867)
HTML 表单最初只支持 application/x-www-form-urlencoded 形式编码(key=value&key=value...),但它不适合用于传输二进制数据(文件)或者包含非ASCII字符的数据。所以 multipart/form-data 就诞生了,专门用于传输文件。
<%@page pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<form method="post" action="./recvfile"
enctype="multipart/form-data">
<label>文本域:</label>
<input type="text" name="textfield"/>
<br/>
<label>单文件:</label>
<input type="file" name="single"/>
<br/>
<label>多文件:</label>
<input type="file" name="multi" multiple/>
<br/>
<label>没文件:</label>
<input type="file" name="empty"/>
<br/>
<button type="submit">提交</button>
</form>
</body>
</html>
2. 文件上传请求响应
2.1. Servlet 3.x(MultipartConfig)
Servlet 3.x 大法好,无需插件,就能处理上传的文件。
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<Part> parts = request.getParts();
Iterator<Part> 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);
}
}
}
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 则是最好的选择。
关键依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
代码示例:
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<FileItem> items = null;
try {
items = upload.parseRequest(request);
} catch (FileUploadException e) {
throw new RuntimeException(e);
}
Iterator<FileItem> 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);
}
}
}
}
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 的实现供:
2.3.1 CommonsMultipartResolver 示例
关键依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
代码示例:web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>xupload</display-name>
<servlet>
<servlet-name>xupload</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>xupload</servlet-name>
<url-pattern>/mvc/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
代码示例:springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="webj2ee"/>
<context:annotation-config/>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
p:defaultEncoding="UTF-8"
p:maxInMemorySize="512"
p:maxUploadSizePerFile="1048576"
p:maxUploadSize="2097152" />
</beans>
代码示例:SpringMVCApacheCommonsUploadController
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<String> fileNames = multipartRequest.getFileNames();
while (fileNames.hasNext()) {
String fieldName = fileNames.next();
List<MultipartFile> 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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>xupload</display-name>
<servlet>
<servlet-name>xupload</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<multipart-config>
<location>d:/temp</location>
<max-file-size>1024</max-file-size>
<max-request-size>2048</max-request-size>
<file-size-threshold>512</file-size-threshold>
</multipart-config>
</servlet>
<servlet-mapping>
<servlet-name>xupload</servlet-name>
<url-pattern>/mvc/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
代码示例:springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="webj2ee"/>
<context:annotation-config/>
<bean id="multipartResolver"
class="org.springframework.web.multipart.support.StandardServletMultipartResolver"/>
</beans>
3. 文件上传请求发起
3.1. Server 端发起(HttpClient)
应用场景:Server 端请求转发;
关键依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.9</version>
</dependency>
代码示例:
package webj2ee;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.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 问题的说明
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ page session="false" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
<!-- 引入 uploadify 组件 -->
<script type="text/javascript" src="./uploadify/jquery.uploadify.js"></script>
<link href="./uploadify/uploadify.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="myMultiFileUpload"></div>
<script>
$("#myMultiFileUpload").uploadify({
swf : './uploadify/uploadify.swf', // 指明flash插件路径
uploader : './apacherecvfile', // 文件上传请求地址
width : 120,
height : 30,
fileObjName : "myfile", // 相当于 <input type="file" name="myfile"
buttonText : "选择文件",
removeCompleted : false,
fileTypeExts : "*.png;*.jpg;*.txt", // 限制可选的文件类型
onUploadComplete: function(file) { // 每个文件上传成功的回调
console.log('[' + file.name + '] upload complete.');
}
});
</script>
</body>
</html>
效果展示:
3.3. 客户端发起 —— Form 表单上传
用 Form 表单上传文件,是最传统的方法,优点就是兼容性特别好。
优点:兼容性好,不需要插件,浏览器原生支持
老古董(IE7/8/9): a. √ 支持上传完成回调机制; b. × 支持多选文件上传; c. × 支持筛选上传文件类型; d. × 支持限定上传文件尺寸; e. × 支持文件上传进度监控; 现代浏览器(>=IE10、Chrome、Firefox): a. √ 支持上传完成回调机制; b. √ 支持多选文件上传; c. √ 支持筛选上传文件类型; d. × 支持限定上传文件尺寸; e. × 支持文件上传进度监控;
缺点:低版本浏览器上,能力偏弱;
代码示例:form_upload_ie8_ie9.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
</head>
<body>
<form action="./recvfileswithcallback?callbackFnName=fileUploadSuccess"
enctype="multipart/form-data"
method="POST"
target="formSubmitResult"
>
<input type="file" name="myfile"/>
<input type="submit" value="submit"/>
<input type="reset" value="reset"/>
</form>
<iframe name="formSubmitResult" style="width:750px; height:200px;"></iframe>
<script>
function fileUploadSuccess(params){
alert("fileUploadSuccess called!\n"+params);
}
</script>
</body>
</html>
代码示例:form_upload_callback_trigger.jsp
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<fieldset>
<legend>submitted files info</legend>
<pre>
${submittedFilesInfo}
</pre>
</fieldset>
<script>
parent.${callbackFnName}("${callbackParams}");
</script>
</body>
</html>
代码示例:RecvFilesWithCallback.java
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<Part> parts = request.getParts();
Iterator<Part> 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)
核心逻辑:
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html><head>
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
</head><body>
<form>
<input id="myfile" type="file" multiple></input>
</form>
<script type="text/javascript">
$("#myfile").bind("change", function(e){
var fileList = this.files;
console.dir(fileList);
for(var i=0; i<fileList.length; i++){
var file = fileList[i];
console.log(file.name + ": " + file.size/1024 + "KB;");
}
});
</script>
</body></html>
效果展示:
例2:获取用户拖拽的文件(FileList、File)
核心逻辑:
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>WebJ2EE FileAPI</title>
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
</head>
<body>
<textarea id="myFileDrop" rows="10"></textarea>
<script type="text/javascript">
$("#myFileDrop").bind("drop", function(e) {
// 阻止冒泡、阻止浏览器默认行为(浏览器默认会自动打开拖拽的文件)
e.stopPropagation();
e.preventDefault();
// 通过 e.originalEvent.dataTransfer.files 获取拖拽进来的 FileList
// (注: 经jquery包装的事件对象e中不包含dataTransfer对象,
// 所以需要通过e.originalEvent访问浏览器的原始事件对象)
var fileMsg = "";
var fileList = e.originalEvent.dataTransfer.files;
for(var i=0; i<fileList.length; i++){
var file = fileList[i];
fileMsg += file.name + ": " + file.size/1024 + "KB;\n";
}
// 将组装后的文件信息展示在textarea组件中
$(this).val(fileMsg);
});
</script>
</body>
</html>
效果展示:
例3:用 FileReader 预览图片
FileReader 是一种异步文件读取机制,用于读取File、Blob中的文件数据。
常用接口:
// 用于将 Blob 或 File 对象,按指定的编码(默认UTF-8),转化为字符串形式;
readAsText(Blob|File, opt_encoding):
// 用于将 Blob 或 File 对象,转换为一个基于base64编码的Data URL对象。(文件上传前预览就是靠这个技术)
readAsDataURL(Blob|File):
核心逻辑:
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>WebJ2EE FileAPI</title>
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
</head>
<body>
<input id="myfile" type="file" accept="image/*" multiple></input>
<div id="previewArea"></div>
<script type="text/javascript">
$("#myfile").bind("change", function(e){
let fileList = this.files; // 获取用户选择的文件列表;
for(let file of fileList ){ // 为每个文件,生成一张预览图;
let reader = new FileReader(); // 构建一个FileReader实例;
// FileReader是异步读取数据,注册onload事件,监听文件读取进度;
reader.addEventListener("load", function () {
// FileReader完成读取后,onload事件被触发,
// 属性 result 中包含换后的 Data URL 对象;
let imageDataUrl = reader.result;
// 构造一个Image结构
let image = new Image();
image.src = imageDataUrl;
$("#previewArea").append( image );
}, false);
reader.readAsDataURL(file); // 将图片转换为基于Base64的Data URL对象;
}
});
</script>
</body>
</html>
效果展示:
例4:用 onprogress 事件监听文件上传进度
XMLHttpRequest Level 2,支持 onprogress 事件,可以监听文件上传或下载中的进度。
核心逻辑:
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html><html><head>
<meta charset="UTF-8"/>
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
<style> *{ font-family:"Consolas"; } </style>
</head><body>
<form>
<div>select a file to upload.</div><br/>
<div><input type="file" id="myfile" onchange="onFileSelected()"/></div><br/>
<fieldset>
<legend>FileInfo:</legend>
fileName: <span id="fileName"></span><br/>
fileSize: <span id="fileSize"></span><br/>
fileType: <span id="fileType"></span><br/>
progress: <span id="progress"></span><br/>
</fieldset><br/>
<input type="button" onclick="uploadFile()" value="Upload"/>
</form>
<script type="text/javascript">
function onFileSelected() {
// file的非multiple模式,FileList中只会有一个元素;
var file = document.getElementById('myfile').files[0];
// 展示fileName、fileSize、fileType
$('#fileName').text(file.name);
$('#fileSize').text(Math.round(file.size / 1024) + 'KB');
$('#fileType').text(file.type);
$('#progress').text("0%");
}
function uploadFile() {
// 1. 获取用户选取的文件
var file = document.getElementById('myfile').files[0];
// 2. 用FormData组件要发送的表单数据(文件)
var fd = new FormData();
fd.append("myfile", file);
// 3. 使用XMLHttpRequest发送请求
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", function(e) {
let computable = e.lengthComputable; // 进度是否可计算
if( computable ){
let loaded = e.loaded; // 已上传量
let total = e.total; // 数据总量
$('#progress').text( Math.round(loaded / total * 100) + '%');
}else {
$('#progress').text( 'unable to compute progress!');
}
}, false);
xhr.upload.addEventListener("load", function(e) { // 传输完成
console.log("Transfer Complete!");
}, false);
xhr.addEventListener("load", function(e) { // 请求完成
console.log(xhr.responseText);
}, false);
xhr.open("POST", "./apacherecvfile");
xhr.send(fd);
}
</script>
</body></html>
效果展示:
例5:FormData + XMLHttpRequest 上传文件
核心逻辑:
代码示例:
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<script type="text/javascript" src="./jquery-1.12.4.js"></script>
<style> *{ font-family:"Consolas"; }</style>
</head>
<body>
<form>
<div>select a file to upload.</div><br/>
<div>
<input type="file"
id="myfile"
multiple="multiple"
accept="image/*"
onchange="onFileSelected()"
/>
</div><br/>
<fieldset>
<legend>selected files info</legend>
<pre id="selectedFilesInfo">
</pre>
</fieldset><br/>
<input type="button" onclick="uploadFile()" value="Upload"/>
</form>
<script type="text/javascript">
function onFileSelected() {
var files = document.getElementById('myfile').files;
var selectedFilesInfo = "";
for(var i=0; i<files.length; i++){
var file = files[i];
var name = file.name;
var contentType = file.type;
var size = file.size;
selectedFilesInfo += name+": "+contentType+", "+size/1000 + "K" + "\n";
}
$("#selectedFilesInfo").text(selectedFilesInfo);
}
function uploadFile() {
// 1. 获取用户选取的文件
var files = document.getElementById('myfile').files;
// 2. 用 FormData 组装 Multipart 型请求
var fd = new FormData();
for(var i=0; i<files.length; i++){
fd.append("myfile", files[i], files[i].name);
}
// 3. 使用XMLHttpRequest Level 2发送请求
// 注:推荐使用 Fetch API 替代 XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", function(e) { // 请求完成
alert("上传完成!");
}, false);
xhr.open("POST", "./HandleUpload");
xhr.send(fd);
}
</script>
</body>
</html>
效果展示:
参考:
RFC 1867: https://tools.ietf.org/html/rfc1867 RFC 6532: https://tools.ietf.org/html/rfc6532 https://caniuse.com/ https://developer.mozilla.org/zh-CN/ 《Servlet、JSP 和 Spring MVC 初学指南》 《Spring 入门经典》