javaWeb文件上传与下载
理论(原理分析):
文件下载
- 要获取下载文件的路径
- 下载的文件名(显示给客户端的文件名)
- 设置想办法让浏览器能够支持下载我们需要的东西(
attachment;filename) - 创建文件输入流
- 创建缓冲区
- 获取
OutputStream对象 - 将
FileOutputStream流写入到buffer缓冲区 - 使用
OutputStream将缓冲区中的数据输出到客户端!
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 要获取下载文件的路径
String realPath = "F:\\班级管理\\西开【19525】\\2、代码\\JavaWeb\\javaweb-02-servlet\\response\\target\\classes\\秦疆.png";
System.out.println("下载文件的路径:"+realPath);
// 2. 下载的文件名是啥?
String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);
// 3. 设置想办法让浏览器能够支持(Content-Disposition)下载我们需要的东西,中文文件名URLEncoder.encode编码,否则有可能乱码
resp.setHeader("Content-Disposition","attachment;filename="+URLEncoder.encode(fileName,"UTF-8"));
// 4. 获取下载文件的输入流
FileInputStream in = new FileInputStream(realPath);
// 5. 创建缓冲区
int len = 0;
byte[] buffer = new byte[1024];
// 6. 获取OutputStream对象
ServletOutputStream out = resp.getOutputStream();
// 7. 将FileOutputStream流写入到buffer缓冲区,使用OutputStream将缓冲区中的数据输出到客户端!
while ((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
in.close();
out.close();
}
注意:设置头时attachment;filename的分号如果不小心写成冒号,则只会显示图片而不是下载图片
文件上传
需要的依赖包
-
commons.io<!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency> -
commons.fileupload(依赖于``commons.io`)<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
需要用到的类
ServletFileUpload 负责处理上传文件数据,并将表单每个输入项封装成一个FileItem`对象,在使用`ServletFileUplead`对象解析请求时需要`DiskFileItemFactory`对象。所以我们需要在进行解析工作前构造好`DiskFileItemFactory`对象,通过`ServletFileUpload`对象的构造方法或`setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
FileItem类
在HTML页面input必须要有name<input type="file" name="filename">
表单如果包含一个文件上传输入项的话,这个表单的enctype属性就必须设置为multipart/form-data
<form action="" enctype="multipart/form-data" method="post">
上传用户<input type="text" name="username"><br/>
<p><input type="file" name="file1"></p>
<p><input type="file" name="file2"></p>
<p><input type="submit"> | <input type="reset"></p>
</form>
常用方法
//isFormField方法用于判断FileItem类地下封装的数据是一个普通文本表单
//还是一个文件表单,如果是普通表单字段则返回true,否则返回false
boolean isFormField();
//getFiledName()方法用于返回表单标签name属性的值。
String getFieldName();
//getString()方法用于将FileItem对象中保存的数据流内容以一个字符串返回
String getString();
//getName()方法用于获得文件上传字段中的文件名
String getName();
//以流的形式返回上传文件的数据内容。
InputStream getInputStream();
//delete方法用来清空FileItem类对象中存放的主体内容
//如果主体内容被保存在临时文件中,delete方法将删除该临时文件。
void delete();
ServletFileUpload类
ServletFileUpload负责处理上传的文件数据,并架构表单中每个输入项封装成一个FileItem对象中,使用其**parseRequest(HttpServletRequest r)**方法可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,然后以List列表的形式返回。使用该方法处理上传文件简单易用。
package com.example.file;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
@WebServlet(name = "FileServlet", value = "/FileServlet")
public class FileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的文件时普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//终止方法运行,说明这是个普通表单,直接返回
}
//创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdir();//创建这个目录
}
//缓存,临时文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpFile = new File(tmpPath);
if (!tmpFile.exists()) {
tmpFile.mkdir();//创建这个临时目录
}
//处理上传的文件
/*
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
*/
DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
ServletFileUpload servletFileUpload = getServletFileUpload(factory);
String result = extracted(request, uploadPath, servletFileUpload);
request.setAttribute("result",result);
System.out.println(result);
request.getRequestDispatcher("info.jsp").forward(request,response);
}
private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
StringBuilder msg = new StringBuilder();
try{
//3.处理上传的文件
//吧前端请求解析 封装成一个FileItem对象
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for (FileItem fileItem : fileItems) {
String name;
if(fileItem.isFormField()){
//getFileName指的是前端表单控件Name
name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(name+":"+value);
}else {//文件
//----------------处理文件-----------------//
//获取文件全名
name = fileItem.getName();
//判断文件大小
if(fileItem.getSize() >= 1024 * 1024 * 10){
msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
fileItem.delete();//上传成功,清除临时文件
continue;
}
//可能存在文件名不合法的情况
if(name == null|| name.trim().equals("")){
fileItem.delete();//上传成功,清除临时文件
continue;
}
//获取上传的文件名
String fileName = name.substring(name.lastIndexOf('/') + 1);
//获取文件后缀
String fileExtName = name.substring(name.lastIndexOf('.') + 1);
/*
如果后缀名不是需要的则直接return不处理
告诉用户文件类型不正确
*/
//可以使用UUID(唯一识别的通识码)保证文件名唯一
//UUID.randomUUID(),随机产生一个识别的通用码
//网络传输中的东西都需要序列化
//实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
//implement Serializable : 标记接口,JVM---> Java栈 本地方法栈 native -->C++
String uuidPath = UUID.randomUUID().toString();
//----------------存放地址-----------------//
//存放位置 uploadPath
//文件真是存在的路径 realPath
String realPath = uploadPath +"/"+uuidPath;
//给每个文件创建一个对应的文件夹
File realPathFile = new File(realPath);
if(!realPathFile.exists()){
realPathFile.mkdir();
}
//----------------文件传输-----------------//
//获得文件上传的流
InputStream is = fileItem.getInputStream();
//创建一个文件输出流
FileOutputStream fos = new FileOutputStream(realPath + "/" + name);
//创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
//判断是否读取完毕
int len =0;
//如果大于0说明还存在数据
while ((len=is.read(buffer))>0){
fos.write(buffer,0,len);
}
//关闭流
fos.close();
is.close();
msg.append(name).append(":上传成功<br>");
fileItem.delete();//上传成功,清除临时文件
}
}
}catch (Exception e){
e.printStackTrace();
return "上传异常";
}
return msg.toString();
}
private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
//2.获取ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
//监听文件上传进度
// servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
//处理乱码问题
servletFileUpload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
// servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
//设置总共能够上传文件的大小
// servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
/*
如果设置了大小则如果出现超过大小的情况 则直接异常
*/
return servletFileUpload;
}
private DiskFileItemFactory getDiskFileItemFactory(File dir) {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
if(dir!=null){
factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
factory.setRepository(dir);
}
return factory;
}
}
不足:
在上述代码中并没有真正限制大小,而是允许文件上传到tmp文件夹中后再进行判断大小是否超额。目前我只能在前端进行判断大小进行限制,后端进行限制会在转换的时候异常,然而该异常又会导致request奔溃,前端收不到对应回复。
实战(项目实战):
项目一览:
-
项目目录

-
项目展示







初始化项目
-
我使用的编辑器:
IDEA2021.2 -
项目创建


依赖注入(pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>File</artifactId>
<version>1.0-SNAPSHOT</version>
<name>File</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<junit.version>5.7.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- JSTL表达式的依赖 -->
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<!-- standard标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
编写(导入)静态资源
-
JSP
-
upload.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <!DOCTYPE html> <html> <head> <title>文件上传</title> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/> </head> <body> <div class="form"> <form id="fileForm" action="${pageContext.request.contextPath}/UploadServlet" enctype="multipart/form-data" method="post"> 上传用户<input type="text" name="username"><br> <br> <div> <label for="files">选择文件上传(可多选)</label> <input type="file" id="files" name="files" multiple="" style="opacity: 0;"> </div> <br> <div class="preview"> <p>当前还没有选中的文件</p> </div> <br> <p> <input id="submitBtn" type="button" value="上传"> | <input type="reset" id="resetBtn"> </p> </form> <a href="${pageContext.request.contextPath }/DownloadServlet?method=getFileNameList"><button>去下载文件</button></a> </div> </body> <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.8.3.min.js"></script> <script type="text/javascript" src="${pageContext.request.contextPath }/js/upload.js"></script> </html> -
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>上传文件</title> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/> </head> <body> <div class="form"> <h2>上传状态:</h2> <div>${result}</div> <a href="${pageContext.request.contextPath }/upload.jsp"><button>去上传文件</button></a> <a href="${pageContext.request.contextPath }/DownloadServlet?method=getFileNameList"><button>去下载文件</button></a> </div> </body> </html> -
download.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE HTML> <html> <head> <title>下载文件</title> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css.css"/> <link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/css/css2.css"/> </head> <body> <div class="form"> <!-- 遍历Map集合 --> <c:forEach var="me" items="${fileNameMap}"> <c:url value="/DownloadServlet" var="downurl"> <c:param name="path" value="${me.key}"></c:param> <c:param name="realName" value="${me.value}"></c:param> </c:url> <a href="${downurl}&method=downloadFile">[下载]</a>-----<a href="${downurl}&method=previewFile">[预览]</a>${me.value}<br/> </c:forEach> <a href="${pageContext.request.contextPath }/upload.jsp"><button>去上传文件</button></a> </div> </body> </html>
-
-
CSS
-
css.css
html { font-family: sans-serif; } .form { width: 580px; background: #ccc; margin: 0 auto; padding: 20px; border: 1px solid black; } form ol { padding-left: 0; } form li, div > p { background: #eee; display: flex; justify-content: space-between; margin-bottom: 10px; list-style-type: none; border: 1px solid black; } form img { height: 64px; order: 1; } form p { line-height: 32px; padding-left: 10px; } form label, form button { background-color: #7F9CCB; padding: 5px 10px; border-radius: 5px; border: 1px ridge black; font-size: 0.8rem; height: auto; } form label:hover, form button:hover { background-color: #2D5BA3; color: white; } form label:active, form button:active { background-color: #0D3F8F; color: white; } -
css2.css
body { padding: 0; margin: 0; } svg:not(:root) { display: block; } .playable-code { background-color: #f4f7f8; border: none; border-left: 6px solid #558abb; border-width: medium medium medium 6px; color: #4d4e53; height: 100px; width: 90%; padding: 10px 10px 0; } .playable-canvas { border: 1px solid #4d4e53; border-radius: 2px; } .playable-buttons { text-align: right; width: 90%; padding: 5px 10px 5px 26px; }
-
-
JavaScript
-
jquery-1.8.3.min.js 这个我就不写进来了,可以到网上下载
-
upload.js
const inputFiles = document.getElementById('files'); const preview = document.querySelector('.preview'); const submitBtn = $("#submitBtn"); const resetBtn = $("#resetBtn"); const fileForm = $("#fileForm"); const maxSize = 1024*1024*10;//10M 单个文件大小限制 inputFiles.style.opacity = 0; inputFiles.addEventListener('change', updateImageDisplay); //可以显示的图片类型 const fileTypes = [ 'image/jpeg', 'image/pjpeg', 'image/png' ]; function validFileType(file) { return fileTypes.includes(file.type); } function returnFileSize(number) { if(number < 1024) { return number + 'bytes'; } else if(number >= 1024 && number < 1048576) { return (number/1024).toFixed(1) + 'KB'; } else if(number >= 1048576) { return (number/1048576).toFixed(1) + 'MB'; } } function updateImageDisplay() { let isOK = true; while(preview.firstChild) { preview.removeChild(preview.firstChild); } const curFiles = inputFiles.files; if(curFiles.length === 0) { const para = document.createElement('p'); para.textContent = '当前还没有选中的文件'; preview.appendChild(para); isOK = false; } else { const list = document.createElement('ol'); preview.appendChild(list); for(const file of curFiles) { const listItem = document.createElement('li'); const para = document.createElement('p'); para.textContent = "文件名称: "+file.name+", 文件大小: "+returnFileSize(file.size); //根据文件大小判断是否超过限制 if(file.size >= maxSize){ para.style.color = "red"; isOK = false; }else { para.style.color = "green"; } //如果是图片类型就显示图片 if(validFileType(file)) { const image = document.createElement('img'); image.src = URL.createObjectURL(file); listItem.appendChild(image); } listItem.appendChild(para); list.appendChild(listItem); } } return isOK; } submitBtn.bind("click",function () { if(updateImageDisplay()){ if(confirm("您确定要将这些文件上传吗?")){ fileForm.submit(); }else { alert("未选中文件或者选中的文件不符合上传要求!"); } } }) resetBtn.bind("click",function () { setTimeout(updateImageDisplay,200); })
-
编写Servlet
web.xml
这里没有写Servlet的映射,直接由Servlet中的@WebServlet(name = "---", value = "/---")注释搞定映射。于是只在web.xml中设置了一个欢迎页面。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--设置欢迎页面-->
<welcome-file-list>
<welcome-file>upload.jsp</welcome-file>
</welcome-file-list>
</web-app>
UploadServlet
package com.example.file;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
@WebServlet(name = "UploadServlet", value = "/UploadServlet")
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的文件时普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//终止方法运行,说明这是个普通表单,直接返回
}
//创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdir();//创建这个目录
}
//缓存,临时文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpFile = new File(tmpPath);
if (!tmpFile.exists()) {
tmpFile.mkdir();//创建这个临时目录
}
//处理上传的文件
/*
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
*/
DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
ServletFileUpload servletFileUpload = getServletFileUpload(factory);
String result = extracted(request, uploadPath, servletFileUpload);
request.setAttribute("result",result);
System.out.println(result);
request.getRequestDispatcher("info.jsp").forward(request,response);
}
private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
StringBuilder msg = new StringBuilder();
try{
//3.处理上传的文件
//吧前端请求解析 封装成一个FileItem对象
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for (FileItem fileItem : fileItems) {
String name;
if(fileItem.isFormField()){
//getFileName指的是前端表单控件Name
name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(name+":"+value);
}else {//文件
//----------------处理文件-----------------//
//获取文件全名
name = fileItem.getName();
//判断文件大小
if(fileItem.getSize() >= 1024 * 1024 * 10){
msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
fileItem.delete();//上传成功,清除临时文件
continue;
}
//可能存在文件名不合法的情况
if(name == null|| name.trim().equals("")){
fileItem.delete();//上传成功,清除临时文件
continue;
}
//获取上传的文件名
String fileName = name.substring(name.lastIndexOf('/') + 1);
//获取文件后缀
String fileExtName = name.substring(name.lastIndexOf('.') + 1);
/*
如果后缀名不是需要的则直接return不处理
告诉用户文件类型不正确
*/
//可以使用UUID(唯一识别的通识码)保证文件名唯一
//UUID.randomUUID(),随机产生一个识别的通用码
//网络传输中的东西都需要序列化
//实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
//implement Serializable : 标记接口,JVM---> Java栈 本地方法栈 native -->C++
String uuidPath = UUID.randomUUID().toString();
//----------------存放地址-----------------//
//存放位置 uploadPath
//文件真实存在的路径 realPath = uploadPath/uuid_fileName
String realPath = uploadPath + "/" +uuidPath + "_" + name;
System.out.println(realPath);
//----------------文件传输-----------------//
//获得文件上传的流
InputStream is = fileItem.getInputStream();
//创建一个文件输出流
FileOutputStream fos = new FileOutputStream(realPath);
//创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
//判断是否读取完毕
int len =0;
//如果大于0说明还存在数据
while ((len=is.read(buffer))>0){
fos.write(buffer,0,len);
}
//关闭流
fos.close();
is.close();
msg.append(name).append(":上传成功<br>");
fileItem.delete();//上传成功,清除临时文件
}
}
}catch (Exception e){
e.printStackTrace();
return "上传异常";
}
return msg.toString();
}
private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
//2.获取ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
//监听文件上传进度
// servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
//处理乱码问题
servletFileUpload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
// servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
//设置总共能够上传文件的大小
// servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
/*
如果设置了大小则如果出现超过大小的情况 则直接异常
*/
return servletFileUpload;
}
private DiskFileItemFactory getDiskFileItemFactory(File dir) {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
if(dir!=null){
factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
factory.setRepository(dir);
}
return factory;
}
}
DownloadServlet
package com.example.file;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
@WebServlet(name = "UploadServlet", value = "/UploadServlet")
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的文件时普通表单还是带文件的表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//终止方法运行,说明这是个普通表单,直接返回
}
//创建上传文件的保存路径 建议保存到WEB-INF路径下 安全 用户无法直接访问上传的文件
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdir();//创建这个目录
}
//缓存,临时文件
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpFile = new File(tmpPath);
if (!tmpFile.exists()) {
tmpFile.mkdir();//创建这个临时目录
}
//处理上传的文件
/*
ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象
所以 Wimbledon需要在进行解析工作前构造好DiskFileItemFactory对象
通过ServletFileUpload对象的构造方法或setFileItemFactory()方法设置ServletFileUpload对象的fileItemFactory属性
*/
DiskFileItemFactory factory = getDiskFileItemFactory(tmpFile);
ServletFileUpload servletFileUpload = getServletFileUpload(factory);
String result = extracted(request, uploadPath, servletFileUpload);
request.setAttribute("result",result);
System.out.println(result);
request.getRequestDispatcher("info.jsp").forward(request,response);
}
private String extracted(HttpServletRequest request, String uploadPath, ServletFileUpload servletFileUpload){
StringBuilder msg = new StringBuilder();
try{
//3.处理上传的文件
//吧前端请求解析 封装成一个FileItem对象
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for (FileItem fileItem : fileItems) {
String name;
if(fileItem.isFormField()){
//getFileName指的是前端表单控件Name
name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");//处理乱码
System.out.println(name+":"+value);
}else {//文件
//----------------处理文件-----------------//
//获取文件全名
name = fileItem.getName();
//判断文件大小
if(fileItem.getSize() >= 1024 * 1024 * 10){
msg.append(name).append(":").append("超过内存限制,拒绝传输<br>");
fileItem.delete();//上传成功,清除临时文件
continue;
}
//可能存在文件名不合法的情况
if(name == null|| name.trim().equals("")){
fileItem.delete();//上传成功,清除临时文件
continue;
}
//获取上传的文件名
String fileName = name.substring(name.lastIndexOf('/') + 1);
//获取文件后缀
String fileExtName = name.substring(name.lastIndexOf('.') + 1);
/*
如果后缀名不是需要的则直接return不处理
告诉用户文件类型不正确
*/
//可以使用UUID(唯一识别的通识码)保证文件名唯一
//UUID.randomUUID(),随机产生一个识别的通用码
//网络传输中的东西都需要序列化
//实体类,如果想要在多个计算机上运行,需要传输(把对象都序列化)
//implement Serializable : 标记接口,JVM---> Java栈 本地方法栈 native -->C++
String uuidPath = UUID.randomUUID().toString();
//----------------存放地址-----------------//
//存放位置 uploadPath
//文件真实存在的路径 realPath = uploadPath/uuid_fileName
String realPath = uploadPath + "/" +uuidPath + "_" + name;
System.out.println(realPath);
//----------------文件传输-----------------//
//获得文件上传的流
InputStream is = fileItem.getInputStream();
//创建一个文件输出流
FileOutputStream fos = new FileOutputStream(realPath);
//创建一个缓冲区
byte[] buffer = new byte[1024 * 1024];
//判断是否读取完毕
int len =0;
//如果大于0说明还存在数据
while ((len=is.read(buffer))>0){
fos.write(buffer,0,len);
}
//关闭流
fos.close();
is.close();
msg.append(name).append(":上传成功<br>");
fileItem.delete();//上传成功,清除临时文件
}
}
}catch (Exception e){
e.printStackTrace();
return "上传异常";
}
return msg.toString();
}
private ServletFileUpload getServletFileUpload(DiskFileItemFactory factory) {
//2.获取ServletFileUpload
ServletFileUpload servletFileUpload = new ServletFileUpload(factory);
//监听文件上传进度
// servletFileUpload.setProgressListener((l, all, i) -> System.out.println("总大小:" + all + "\t已上传:" + l));
//处理乱码问题
servletFileUpload.setHeaderEncoding("UTF-8");
//设置单个文件的最大值
// servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
//设置总共能够上传文件的大小
// servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
/*
如果设置了大小则如果出现超过大小的情况 则直接异常
*/
return servletFileUpload;
}
private DiskFileItemFactory getDiskFileItemFactory(File dir) {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
if(dir!=null){
factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
factory.setRepository(dir);
}
return factory;
}
}
迷糊与疑问
- 当我给ServletFileUpload设置了大小限制时,如果前端传入的文件超过了限制会出现异常,不是普通的异常,它会导致request直接坏掉,后续进行转发与重定向时都不能得到想要的效果,参考网上博客(捕捉异常 在catch块中转发、重定向)依然无法解决问题。
参考与致谢
oad servletFileUpload = new ServletFileUpload(factory);
//监听文件上传进度
// servletFileUpload.setProgressListener((l, all, i) -> System.out.println(“总大小:” + all + “\t已上传:” + l));
//处理乱码问题
servletFileUpload.setHeaderEncoding(“UTF-8”);
//设置单个文件的最大值
// servletFileUpload.setFileSizeMax(1024 * 1024 * 10);//10M
//设置总共能够上传文件的大小
// servletFileUpload.setSizeMax(1024 * 1024 * 10 * 2);
/*
如果设置了大小则如果出现超过大小的情况 则直接异常
*/
return servletFileUpload;
}
private DiskFileItemFactory getDiskFileItemFactory(File dir) {
//1.创建DiskFileItemFactory对象,处理文件上传路径或者大小限制
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区时,将他放到临时文件中
if(dir!=null){
factory.setSizeThreshold(1024 * 1024);//缓存区大小为1M
factory.setRepository(dir);
}
return factory;
}
}
### 迷糊与疑问
1. 当我给ServletFileUpload设置了大小限制时,如果前端传入的文件超过了限制会出现异常,不是普通的异常,它会导致request直接**坏掉**,后续进行转发与重定向时都不能得到想要的效果,参考网上博客(捕捉异常 在catch块中转发、重定向)依然无法解决问题。
### 参考与致谢
1. 感谢 B站狂神说 提供的javaweb学习视频,本文参考该视频编写
本文详细介绍了JavaWeb中文件上传与下载的原理和实战步骤。在文件下载部分,讲解了如何设置下载路径、文件名以及利用缓冲区实现文件下载。文件上传部分讨论了所需的依赖、关键类的使用,如`Commons IO`库,以及前端限制文件大小的重要性,因为后端限制可能导致请求崩溃。实战部分包括项目结构、依赖注入、Servlet编写等,同时提到了在设置上传大小限制时遇到的问题及其解决困扰。
1458

被折叠的 条评论
为什么被折叠?



