最近在工作中遇到文件上传下载问题,因此就在这里记一下
在文件上传下载中,文件是已流的方式在浏览器和服务器之间进行传递的。在apache的开源工具中common-fileupload这个组件是专门来处理文件上传下载的,也是目前使用的最多的一种方式,在这需要导入common-fileupload和common-io两个jar包,好了,下面来走起
一、文件上传
package com.file.Servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
/*
*
* Java、js文件上传(无框架)
*/
public class FileUploadServlet extends HttpServlet {
/*注意的小细节问题
* 1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下(服务器硬盘下)。
* 2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
* 3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
* 4、要限制上传文件的最大值。
* 5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置字符编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
/** 设置响应头允许ajax跨域访问 **/
response.setHeader("Access-Control-Allow-Origin", "*");
/* 星号表示所有的异域请求都可以接受, */
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
// 检测是否为多媒体上传
if (!ServletFileUpload.isMultipartContent(request)) {
// 如果不是则停止
out.write("{'error': '表单必须包含 enctype=multipart/form-data'}");
out.flush();
return;
}
System.out.println("------------------------");
//得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//设置上传时生成的临时文件保存目录
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tempFile = new File(tempPath);
//判断文件上传目录是否存在,不存在则创建
if(!tempFile.exists()) {
//创建文件目录
tempFile.mkdirs();
}
//配置文件上传
try {
//使用Apache文件上传组件处理文件上传步骤:
//1、创建DiskFileItemFactory工厂
DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//1.2、设置工厂缓存区大小,当上传文件大小超过工厂缓存区大小时,就会生成临时文件存放在临时目录中
diskFileItemFactory.setSizeThreshold(1024 * 1024 * 3);//设置缓冲区的大小为3MB,如果不指定,那么缓冲区的大小默认是10KB
//1.3、设置上传临时文件的保存目录
diskFileItemFactory.setRepository(tempFile);
//2、创建文件解析器
ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
//2.1、解决上传文件编码问题
servletFileUpload.setHeaderEncoding("utf-8");
//2.2、监听文件上传进度
servletFileUpload.setProgressListener(new ProgressListener() {
@Override
public void update(long readNum, long fileSize, int arg2) {
//System.out.println("文件大小:"+fileSize+",当前已处理:"+readNum);
}
});
//2.3、设置上传单个文件的大小的最大值(40MB)
servletFileUpload.setFileSizeMax(1024 * 1024 * 40);
//2.4、设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和(60MB)
servletFileUpload.setSizeMax(1024*1024*60);
//3、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
List<FileItem> listItems = servletFileUpload.parseRequest(request);
//判断文件中是否存放数据
if(listItems.size() > 0 && listItems != null) {
//3.1、迭代表单数据
for(FileItem item : listItems) {
//3.2如果fileitem中封装的是普通输入项的数据(form表单中的不是上传文件的其他参数)
if(item.isFormField()) {
String fieldName = item.getFieldName();
//解决普通输入项的数据的中文乱码问题
String value = item.getString("UTF-8");
System.out.println("fieldName:"+fieldName+",value:"+value);
}else {
//3.3、如果fileitem中封装的是上传文件
//3.3.1、得到上传的文件名称,
String fileName = item.getName();
if(fileName == null || fileName.trim().equals("")) {
continue;
}
//注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如: c:\a\b\1.txt,
//而有些只是单纯的文件名,如:1.txt
//3.3.2、处理获取到的上传文件的文件名的路径部分,只保留文件名部分
fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
//得到上传文件的扩展名(可用来限制上传的文件类型)
String fileExitName = fileName.substring(fileName.lastIndexOf(".")+1);
System.out.println("上传的文件的扩展名是:"+fileExitName);
//得到文件唯一的名称
String onlyFileName = onlyFileName(fileName);
//得到文件存储目录
String onlyFilePath = onlyFilePath(onlyFileName,savePath);
System.out.println("文件存储目录:"+onlyFilePath+File.separator+onlyFileName);
//3.3.3、获取item中的上传文件的输入流
InputStream is = item.getInputStream();
//创建输出流
FileOutputStream fos = new FileOutputStream(new File(onlyFilePath+File.separator+onlyFileName));
//创建一个缓冲区
byte buffer[] = new byte[1024];
//判断输入流中的数据是否已经读完的标识
int len = 0;
//循环将输入流读入到缓冲区当中,(len=is.read(buffer))>0就表示in里面还有数据
while((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
fos.flush();
}
//关闭输入输出流
is.close();
fos.close();
//删除处理文件上传时生成的临时文件
item.delete();
}
}// end for
}//end if
}catch(Exception e) {
System.out.println("文件上传失败");
}
}
/**
* @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
* @param fileName 原始文件名
* @return 新文件名
*/
public String onlyFileName(String fileName) {
return UUID.randomUUID()+"_"+fileName;
}
/**
* @Description: 为防止一个目录下面出现太多文件,要使用hash算法打散存储
* @param fileName 文件名,要根据文件名生成存储目录
* @param filePath 文件存储路径
* @return 新的存储目录
*/
public String onlyFilePath(String fileName,String filePath) {
//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
int hashCode = fileName.hashCode();
int addr1 = hashCode&0xf;
int addr2 = (hashCode&0xf)>>4;
//构造新的保存目录
String path=filePath+File.separator+addr1+File.separator+addr2;
File file = new File(path);
//如果目录不存在则创建文件
if(!file.exists()) {
file.mkdirs();
}
return path;
}
}
二、文件下载
package com.file.Servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
*
* Java、js文件下载(无框架)
*/
public class FileDownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置字符编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("multipart/form-data");
/** 设置响应头允许ajax跨域访问 **/
response.setHeader("Access-Control-Allow-Origin", "*");
/* 星号表示所有的异域请求都可以接受, */
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
//1、得到要下载的文件名(经过UUID得到的文件名)
String fileName = request.getParameter("fileName");
//2、得到将要下载文件的目录
String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//3、通过文件名找出文件的所在目录(也可以在上传时存放在数据库,现在去查找数据库)
String findOnlyFilePath = findOnlyFilePath(fileName,savePath);
File file = new File(findOnlyFilePath+File.separator+fileName);
if(!file.exists()) {
System.out.println("您要下载的资源已被删除!!!");
return;
}
//4、处理文件名(因为文件名是经过唯一的处理的所以在这里需要处理一下)
String realname = fileName.substring(fileName.indexOf("_")+1);
//5、设置响应头,控制浏览器下载该文件
response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(realname, "UTF-8"));
//6、读取要下载的文件,保存到文件输入流
FileInputStream fis = new FileInputStream(file);
//创建输出流
OutputStream os = response.getOutputStream();
//创建缓冲区
byte buffer[] = new byte[1024];
int len = 0;
//循环将输入流中的内容读取到缓冲区中
while((len=fis.read(buffer)) > 0) {
//输出缓冲区的内容到浏览器,实现文件下载
os.write(buffer,0,len);
os.flush();
}
//7、关闭输入流输出流
fis.close();
os.close();
}
/**
* @Description: 通过文件名和存储上传文件根目录找出要下载的文件的所在路径
* @param fileName 文件名,要根据文件名获得存储目录
* @param filePath 文件存储路径
* @return 新的存储目录
*/
public String findOnlyFilePath(String fileName,String filePath) {
//得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
int hashCode = fileName.hashCode();
int addr1 = hashCode&0xf;
int addr2 = (hashCode&0xf)>>4;
//构造新的保存目录
String path=filePath+File.separator+addr1+File.separator+addr2;
File file = new File(path);
//如果目录不存在则创建文件
if(!file.exists()) {
file.mkdirs();
}
return path;
}
}
三、文件查询(这里可以查询数据库也可以根据文件目录查询本地文件,这里是第二种)
package com.file.Servlet;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
public class SearchFileServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置编码
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
//设置ajax跨域访问
response.setHeader("Access-Control-Allow-Origin", "*");
/* 星号表示所有的异域请求都可以接受, */
response.setHeader("Access-Control-Allow-Methods", "GET,POST");
String username = request.getParameter("username");
System.out.println(username);
//获取上传文件的目录
String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");
//存储经过UUID生成的唯一文件名、实际的文件名
Map<String,String> fileNameMap = new HashMap<String,String>();
//创建上传文件的目录
File file = new File(uploadFilePath);
//递归遍历filepath目录下的所有文件和目录,将文件的文件名存储到map集合中
traverseFile(file,fileNameMap);
//将数据转成json格式返回前台
JSONObject jsonObject = JSONObject.fromObject(fileNameMap);
out.write(jsonObject.toString());
}
public void traverseFile(File file,Map<String,String> map) {
//如果file代表的不是一个文件,而是一个目录
if(!file.isFile()) {
//列出该目录下的所有文件和目录
File[] listFiles = file.listFiles();
//递归遍历
for(File fil:listFiles) {
traverseFile(fil,map);
}
}else {//如果file代表的是一个文件,而不是一个目录
//处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分(文件名类似于:9349249849-88343-8344_不差钱儿.avi)
String fileName = file.getName().substring(file.getName().indexOf("_")+1);
//file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复,因此作为值
map.put(file.getName(), fileName);
}
}
}
四、xml配置
<!-- 文件上传 -->
<servlet>
<servlet-name>fileUpload</servlet-name>
<servlet-class>com.file.Servlet.FileUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>fileUpload</servlet-name>
<url-pattern>/fileUpload</url-pattern>
</servlet-mapping>
<!-- 文件下载 -->
<servlet>
<servlet-name>fileDownload</servlet-name>
<servlet-class>com.file.Servlet.FileDownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>fileDownload</servlet-name>
<url-pattern>/fileDownload</url-pattern>
</servlet-mapping>
<!-- 文件查询 -->
<servlet>
<servlet-name>fileSearch</servlet-name>
<servlet-class>com.file.Servlet.SearchFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>fileSearch</servlet-name>
<url-pattern>/fileSearch</url-pattern>
</servlet-mapping>
好了,上面就是Java文件上传下载的全部了。
配合Java使用的js文件上传下载:https://blog.csdn.net/little__SuperMan/article/details/82900820