文件上传和下载
第一节 文件上传
1.1 文件上传三要素
-
提供form表单,method必须是post
-
form表单的enctype必须是multipart/form-data
-
提供 input type="file" 类型输入
1.2 实现文件上传
编写工具类:
import java.io.File;
import java.util.HashMap;
import java.util.UUID;
public class UpLoadUtils {
// 新的文件名
public static String createNewFileName(String fileName){
String s = UUID.randomUUID().toString().replace("-", "");
return s+"_"+fileName;
}
//新路径
public static String createNewPath(String basepath,String fileName){
int hashCode =fileName.hashCode();// 0-15
int dir1= hashCode & 0xf;
final int dir2 = hashCode & 0xf0 >> 4;
String newpath=basepath+"/"+dir1+"/"+dir2;
File dir=new File(newpath);
if (!dir.exists()){
dir.mkdirs();
}
return newpath;
}
public static void listFile(File dir, HashMap<String,String> filemap){
File[] files = dir.listFiles();
if (files!=null){
for (File file : files) {
if (file.isDirectory()){
listFile(file, filemap);
}else{
filemap.put(file.getName(), file.getName().split("_")[1]);
}
}
}
}
}
上传文件:
import com.vince.utiles.UpLoadUtils;
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.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@WebServlet(name = "FileUploadServlet",value = "/upload")
@MultipartConfig(maxFileSize = 1024*1024*5,maxRequestSize = 1024*2024*50)
public class FileUploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//乱码
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//获取内容
Collection<Part> parts = request.getParts();
String basepath = request.getServletContext().getRealPath("/WEB-INF/upload");
//创建文件夹
File dir=new File(basepath);
if (!dir.exists()){
dir.mkdirs();
}
//遍历集合
if (parts!=null){
for (Part part : parts) {
// 判断是文件还是普通项
if (part.getSubmittedFileName()==null){
// 普通字段
System.out.println(part.getName()+":"+request.getParameter("part.getName()"));
}else{
String getSubmitFileName= part.getSubmittedFileName();
if (getSubmitFileName==""){
continue;
}
// 兼任IE 火狐 ,chrome
// 获取响应头
String header = part.getHeader("content-disposition");
System.out.println(header);
// 截取
//获取文件名
String filename = header.substring(header.indexOf("filename") + 10, header.length() - 1); // 00.jpg
// 获取后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1);
// 过滤
List<String> allow = Arrays.asList("jpg", "png", "bmp");
if (!allow.contains(ext)){
response.getWriter().write("文件类型不支持");
return;
}
part.write( UpLoadUtils.createNewPath(basepath,filename)+"/"+ UpLoadUtils.createNewFileName(filename));
part.delete(); // 删除缓存
System.out.println("上传成功:"+filename);
response.getWriter().write("上传成功");
}
}
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
1.3 文件上传细节注意
上述的代码虽然可以成功将文件上传到服务器上面的指定目录当中,但是文件上传功能有许多需要注意的小细节问题,以下列出的几点需要特别注意的
1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。
3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。
4、要限制上传文件的最大值。
5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
第二节 文件下载
我们要将Web应用系统中的文件资源提供给用户进行下载,首先我们要有一个页面列出上传文件目录下的所有文件,当用户点击文件下载超链接时就进行下载操作,编写一个ListFileServlet,用于列出Web应用系统中所有下载文件
文件下载:
import com.vince.utiles.UpLoadUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet(name = "DownLoadServlet",value = "/download")
public class DownLoadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String uuid_filename = request.getParameter("filename");
String filename= uuid_filename.split("_")[1];
String basepath = request.getServletContext().getRealPath("/WEB-INF/upload");
String newPath = UpLoadUtils.createNewPath(basepath, filename);
FileInputStream is=new FileInputStream(newPath+"/"+uuid_filename);
int len=-1;
byte[] buf=new byte[1024*1024];
ServletOutputStream os = response.getOutputStream();
// response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(filename, "utf-8"));
response.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode("filename", "utf-8"));
while ((len=is.read(buf))!=-1){
os.write(buf, 0, len);
}
is.close();
os.close();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
ListFileServlet中listfile方法,listfile方法是用来列出目录下的所有文件的,listfile方法内部用到了递归,在实际开发当中,我们肯定会在数据库创建一张表,里面会存储上传的文件名以及文件的具体存放目录,我们通过查询表就可以知道文件的具体存放目录,是不需要用到递归操作的,这个例子是因为没有使用数据库存储上传的文件名和文件的具体存放位置,而上传文件的存放位置又使用了散列算法打散存放,所以需要用到递归,在递归时,将获取到的文件名存放到从外面传递到listfile方法里面的Map集合当中,这样就可以保证所有的文件都存放在同一个Map集合当中。