java文件的上传与下载
1、文件上传下载
1.1 文件上传
什么是文件上传?
要将客户端(浏览器)大数据存储到服务器端,不将数据直接存储到数据库中,而是要将数据存储到服务器所在的磁盘上,这就要使用文件上传。
为什么使用文件上传?
通过文件上传,可以将浏览器端的大数据直接保存到服务器端。不将数据保存到数据库中,而是保存到服务器磁盘上,这样减少了数据库服务器的压力,对数据的操作更加灵活
1.2 commons-fileupload介绍
Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
1.2.1 fileupload的核心类有:
DiskFileItemFactory类
1.设置缓存大小
DiskFileItemFactory factory = new DiskFileItemFactory()
factory.setSizeThreshold(1024*1024); //设置为1m 默认是10k
2.设置临时文件存储位置
File temp=new File(this.getServletContext().getRealPath("/temp"));
factory.setRepository(temp); //可以指定 临时文件存储位置,默认是系统的临时文件 存储位置
ServletFileUpload类
1.parseRequest
方法
List<FileItem> pareRequest(HttpServletRequest r equest)
- 该方法得到所有的上传信息,将每一部分映射成FileItem对象
2.isMultipartContent
方法
boolean isMultipartContent(HttpServletRequest request)
- 这个方法返回值是boolean,它是用于判断当前表单是否是一个上传的表单,简单说,就判断它的encType的值是否是 multipart/form-data
3.setHeaderEncoding方法
- 用于解决上传文件名称中文乱码问题
4.设置上传文件大小
void setFileSizeMax(long fileSizeMax)
设置单个文件上传大小void setSizeMax(long sizeMax)
设置总文件上传大小
FileItem类
1.isFormField
方法
- 这个方法返回的是boolean类型,它是判断当前组件是否是上传组件简单说,就是判断
type="file"
,如果返回true
,代表不是上传组件,返回false
,代表是上传组件
2.getName
方法
- 获取非上传组件的上传文件的名称,如果是非上传组件,返回的是null
3.getFieldName
方法
- 获取组件名称,简单说,就是表单的元素的name值。
4.getString
方法
- 获取非上传组件的value值
getString()
有一个重载的方法getString(String encoding)
可以解决乱码问题
5.void write(File)
方法
- 把上传的文件保存到指定文件中
1.2.2 使用fileupload核心类的步骤如下:
1.创建工厂类DiskFileItemFactory对象:
DiskFileItemFactory factory = new DiskFileItemFactory();
2.使用工厂创建解析器对象:
ServletFileUpload fileUpload = new ServletFileUpload(factory);
3.使用解析器来解析request对象:
List<FileItem> list = fileUpload.parseRequest(request);
1.3 文件上传细节
1.3.1 文件重名问题
上传操作文件重名问题分析
- 每一个客户端都可以进行文件上传操作,那么当我们上传的文件过多,一定会出现同名的文件,那么在服务器端只能保存一个,对于这个问题,我们在上传文件时,就需要考虑文件重名问题
上传操作文件重名解决方案
一般情况下,对于上传文件,为了保证不重名,会给文件起一个随机名.
- 一种方案是使用uuid
- 一种方案是使用毫秒值
传统的Servlet文件上传
1)导入依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2)编写表单
文件上传的必要前提:
表单文件上传的三个要素:
- form标签要添加enctype="multipart/form-data"
- 提交方法为post
- input类型为file
<form action="/upload" method="post" enctype="multipart/form-data">
...
<input type="file" name="file" >
...
</form>
1.4 servlet中上传主要代码实现
1.4.1 原生1.0版本
@WebServlet("/Upload")
public class UploadServlet extends HttpServlet {
//多文件上传
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
servletFileUpload.setHeaderEncoding("utf-8");
List<String> filenames = new ArrayList<>();
try {
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for (FileItem fileItem : fileItems) {
if(fileItem.isFormField()){
String string = fileItem.getString();
String fieldName = fileItem.getFieldName();
System.out.println(fieldName+" "+string);
}else{
filenames.add(uploadFile(fileItem));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
request.setAttribute("filenames",filenames);
request.getRequestDispatcher("upload.jsp").forward(request,response);
}
// 将上传文件表单项封装
private String uploadFile(FileItem fileItem) {
// 如果上传表单项
// 得到文件输入流
// 创建物理目录路径
String realPath = this.getServletContext().getRealPath("/upload");
// 根据该路径创建一个目录对象
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();// 创建一个指定的目录
}
// 得到上传的名子
String filename = fileItem.getName();// 美女.jpg
if (filename != null) {
// 得到文件后缀
String extend = filename.substring(filename.indexOf("."));
System.out.println(extend);
// 重写生成一个唯一的文件名
filename = UUID.randomUUID() + extend;
}
// 上传文件,自动删除临时文件
try {
fileItem.write(new File(realPath, "/" + filename));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return filename;
}
}
1.4.2 2.0版本-基于java反射改造
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
servletFileUpload.setHeaderEncoding("UTF-8");
Book book = new Book();
try {
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
for(FileItem fileItem : fileItems){
if(fileItem.isFormField()){
//动态获取类的方法
String method = "set"+fileItem.getFieldName().substring(0,1).toUpperCase()+fileItem.getFieldName().substring(1);
Method method1 = book.getClass().getMethod(method,String.class);
method1.invoke(book, fileItem.getString());
}else{
book.setImage(uploadFile(fileItem));
}
}
System.out.println(book);
//执行添加数据操作
int i = bookService.addBook(book);
if(i == 1){
response.sendRedirect("BookListServlet");
}else{
request.getRequestDispatcher("addBook.jsp?m=af").forward(request,response);
}
}catch (Exception e){
e.printStackTrace();
}
}
// 将上传文件表单项封装
private String uploadFile(FileItem fileItem) {
// 如果上传表单项
// 得到文件输入流
// 创建物理目录路径
String realPath = this.getServletContext().getRealPath("/img");
// 根据该路径创建一个目录对象
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();// 创建一个指定的目录
}
// 得到上传的名子
String filename = fileItem.getName();// 美女.jpg
if (filename != null) {
// 得到文件后缀
String extend = filename.substring(filename.indexOf("."));
System.out.println(extend);
// 重写生成一个唯一的文件名
filename = UUID.randomUUID() + extend;
}
// 上传文件,自动删除临时文件
try {
fileItem.write(new File(realPath, "/" + filename));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return filename;
}
1.4.3 3.0版本-基于ajax异步文件上传
使用异步上传图片,只负责照片的上传,返回图片的名称,然后其他表单参数所有都通过form表单上传
/**
* 上传图片
*
* @param request
* @param response
* @throws Exception
*/
public void upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 创建一个DiskFileItemfactory工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
// 创建一个ServletFileUpload核心对象
ServletFileUpload sfu = new ServletFileUpload(factory);
// 解决上传文件名中文乱码
sfu.setHeaderEncoding("utf-8");
String filename = "";
// 解析request对象
try {
FileItem fileItem = sfu.parseRequest(request).get(0);
filename = uploadFile(fileItem);
response.getWriter().print(filename);
} catch (Exception e) {
// TODO Auto-generated catch block
response.getWriter().print("N");
}
}
/**
* 将上传文件表单项封装
*
* @param fileItem
* @return
*/
private String uploadFile(FileItem fileItem) {
// 如果上传表单项
// 得到文件输入流
// 创建物理目录路径
String realPath = this.getServletContext().getRealPath("/upload");
// 根据该路径创建一个目录对象
File dir = new File(realPath);
if (!dir.exists()) {
dir.mkdirs();// 创建一个指定的目录
}
// 得到上传的名子
String filename = fileItem.getName();
if (filename != null) {
// 得到文件后缀
String extend = filename.substring(filename.indexOf("."));
System.out.println(extend);
// 重写生成一个唯一的文件名
filename = UUID.randomUUID() + extend;
}
// 上传文件,自动删除临时文件
try {
fileItem.write(new File(realPath, "/" + filename));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return filename;
}
/**
* 上传图片
*/
const uploadPic = ()=> {
if ($("#pic").val() == '') {
return;
}
let formData = new FormData();
formData.append('pic', $('#pic')[0].files[0]);
$.ajax({
url : "upload/uploadPic",
type : "post",
data : formData,
contentType : false,
processData : false,
success : function(data) {
pic = data;
}
});
}
1.4.5 4.0版本-springmvc文件上传
springmvc单文件上传
前端form表单
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="upload" >
<button>上传</button>
</form>
上传方法中使用MultipartFile参数获得上传文件
@RequestMapping("/upload")
public String fileupload(HttpServletRequest request, MultipartFile upload) throws Exception {
//获取项目目录下的upload目录路径
String path = request.getSession().getServletContext().getRealPath("/upload/");
System.out.println(path);
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
//获取上传文件名字
String filename = upload.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
//创建唯一的文件名
String uuid= UUID.randomUUID().toString().replace("-","");
filename = uuid + suffix;
//完成文件上传
upload.transferTo(new File(path,filename));
return "success";
}
需要在springmvc的配置中添加上传处理器
<!--上传处理器-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--上传文件最大字节数-->
<property name="maxUploadSize" value="10485760"/>
</bean>
SpringMVC多文件上传
给表单添加多个文件项
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploads">
<input type="file" name="uploads">
<input type="file" name="uploads">
<input type="submit" value="MVC多文件上传">
</form>
添加MultipartFile数组为参数,参数名和表单name一致
@RequestMapping("/upload3")
public String fileupload3(HttpServletRequest request, MultipartFile[] uploads) throws Exception {
//获取tomcat项目目录下的upload目录路径
String path = request.getSession().getServletContext().getRealPath("/upload/");
System.out.println(path);
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
for(MultipartFile upload : uploads) {
//获取上传文件名字
String filename = upload.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
//创建唯一的文件名
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "-" + suffix;
//完成文件上传
upload.transferTo(new File(path, filename));
}
return "success";
}
SpringMVC其它上传类
SpringMVC还提供了其它API支持上传:
- CommonsMultipartResolver 多部分解析器,用于判断请求是否存在上传文件
- MultipartHttpServletRequest 多部分请求,用于获得文件相关信息
具体用法:
@RequestMapping("upload4")
public String fileupload4(HttpServletRequest request) throws IllegalStateException, IOException
{
String path = request.getSession().getServletContext().getRealPath("/upload/");
//创建多部分解析器
CommonsMultipartResolver multipartResolver=new CommonsMultipartResolver(
request.getSession().getServletContext());
//检查表单是否支持文件上传
if(multipartResolver.isMultipart(request)){
//将请求转换为多部分请求,支持上传
MultipartHttpServletRequest multiRequest=(MultipartHttpServletRequest)request;
//获取请求中所有的文件名
Iterator iter=multiRequest.getFileNames();
while(iter.hasNext()) {
//遍历所有文件
MultipartFile file=multiRequest.getFile(iter.next().toString());
if(file!=null) {
//获取上传文件名字
String filename = file.getOriginalFilename();
String suffix = filename.substring(filename.lastIndexOf("."));
//创建唯一的文件名
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + suffix;
//上传
file.transferTo(new File(path,filename));
}
}
}
return "success";
}
1.5 文件下载
/**
* 下载控制器
*/
@Controller
public class DownloadController {
@RequestMapping("download")
public void download(String file, HttpServletResponse response) throws IOException {
//下载文件的路径
String path = "D:\\install\\";
File downFile = new File(path+file);
if(downFile.exists()){
//设置浏览器下载内容类型,响应头
response.setContentType("application/x-msdownload");
response.setHeader("Content-Disposition","attachment;filename="+file);
//通过流发送文件
Files.copy(downFile.toPath(),response.getOutputStream());
}
}
}
参考文章:http://blog.ncmem.com/wordpress/2023/12/12/java%e6%96%87%e4%bb%b6%e7%9a%84%e4%b8%8a%e4%bc%a0%e4%b8%8e%e4%b8%8b%e8%bd%bd/
欢迎入群一起讨论