1. 需求
1.1 文件的上传
1.1.1 可以上传一个或多个文件,每个文件后面附加一个desc文本域,用于上传对该文件的说明信息
>
1.1.2 文件通过表单上传到服务器,服务器将文件保存到它的本地磁盘文件夹,并将其文件名改为一个随机数,以免有重名文件冲突;并将该文件的原文件名、保存在服务器上的路径(包含随机文件名) 以及对该文件的描述信息保存到数据库中
1.1.3 删除临时存储在磁盘上的文件(对于上传的大于5M的文件,先存在磁盘的临时目录下,而非内存中)。
1.1.4 重定向到 list.jsp--显示所有上传文件的页面
1.2 文件的下载
1.2.1 指定客户端浏览器的响应方式为文件下载的方式 , 通知浏览器下载的文件不再由浏览器直接打开,而是由用户手动操作
1.2.2 根据 list 页面提供的 id ,从数据库获取要下载的文件的信息;
1.2.3 由 path 字段获取该文件在服务器上的存储位置,并创建 File 对象;
1.2.4 创建输入输出流: 输入流对应 下载文件对象 file; 输出流:客户端浏览器的输出流response.getOutputStream();
将文件写到输出流,完成下载工作。
2. 主要知识点
2.1 HTML <form> 标签的 enctype 属性
> 定义和用法 :
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。
默认地,表单数据会编码为 "application/x-www-form-urlencoded"。就是说,在发送到服务器之
前,所有字符都会进行编码(空格转换为 "+" 加号,特殊符号转换为 ASCII HEX 值)。
> 属性值 :
值 | 描述 |
application/x-www-form-urlencoded | 在发送前编码所有字符(默认) |
multipart/form-data | 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。 |
text/plain | 空格转换为 "+" 加号,但不对特殊字符编码。 |
> 语法 :
<form action="uploadServlet" method="post" enctype="multipart/form-data">
File:<input type="file" name="fileupload" /><br><br>
...
</form>
2.2 common-fileupload 组件
> 说明:
common-fileupload 组件是apache的一个开源项目之一,可以从http://jakarta.apache.org/commons/fileupload/下载。用该组件可实现一次上传一个或多个文件,并可限制文件大小。
> 用法 :
>> 导入两个包 :commons-fileupload-1.3.1.jar 和commons-io-2.4.jar
>> 简单用法:解析request,获取所有表单提交的 input 项,存放在 List<FileItem> 对象中,
其中一个input项对应一个 FileItem 对象
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory(); // Configure a repository (to ensure a secure temp location is used)
// 上传文件的临时存放位置 ServletContext servletContext = this.getServletConfig().getServletContext(); File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir"); factory.setRepository(repository); // Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory); // Parse the request, 解析 request; 从request中获取表单提交的所有 input 项,
// 并将其存放在 List<FileItem> 对象 List<FileItem> items = upload.parseRequest(request);
}
>> 更多操作 :设置最大内存存储文件大小,临时文件夹路径,request最多上传文件总大小
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Set factory constraints
// 设置最大内存容量:超过该值,则将文件存放到临时文件夹
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
// Set overall request size constraint
// 设置一个request 中最多上传的文件的总大小,单位为Byte
upload.setSizeMax(yourMaxRequestSize);
// Parse the request
List<FileItem> items = upload.parseRequest(request);
>> 处理上传的 items
2.3 文件下载 :// Process the uploaded items Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); // 处理文本信息 if (item.isFormField()) { processFormField(item); String name = item.getFieldName(); String value = item.getString(); ...
} // 处理二进制文件 else { String fieldName = item.getFieldName(); String fileName = item.getName(); String contentType = item.getContentType(); boolean isInMemory = item.isInMemory(); long sizeInBytes = item.getSize();
// 存储到服务器的本地磁盘文件夹 if (writeToFile) { File uploadedFile = new File(...); item.write(uploadedFile); }
// 若要写入数据库,需要获取上传文件的输入流
else { InputStream uploadedStream = item.getInputStream(); ... uploadedStream.close(); } } }
> //3. 指定客户端浏览器的响应方式为文件下载的方式
response.setContentType("application/x-msdownload");
// 通知浏览器下载的文件不再由浏览器直接打开,而是由用户手动操作
response.setHeader("Content-Disposition","attachment; filename="+fileName);
public class DownloadServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String idStr = request.getParameter("id"); int id = Integer.parseInt(idStr); //1. 从数据库获取要下载文件的信息 FileUploadBean fileInfo = FileDaoImpl.getInstance().getFileInfo(id); //2. 将文件名由 UTF-8 编码转成 ISO_8859_1 编码,以免浏览器解析乱码 String fileName = new String(fileInfo.getName().getBytes("UTF-8"),"ISO_8859_1"); //3. 指定客户端浏览器的响应方式为文件下载的方式 response.setContentType("application/x-msdownload"); // 通知浏览器下载的文件不再由浏览器直接打开,而是由用户手动操作 response.setHeader("Content-Disposition","attachment; filename="+fileName); //4. 获取该文件在服务器上的存储路径,并创建 File 对象 File file = new File(fileInfo.getPath()); //5. 创建输入流和输出流, 输入流对应 下载文件对象 file; 输出流:response.getOutputStream(); //完成文件从服务器下载到本地的功能 InputStream in = null; ServletOutputStream out = null; try { in = new FileInputStream(file); out = response.getOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while((len = in.read(buffer)) != -1){ out.write(buffer, 0, len); } } catch (Exception e) { e.printStackTrace(); } finally { if(out != null) out.close(); if(in != null) in.close(); } }
3. 注意事项
3.1 文件合法性检查 :对文件的后缀名,大小做检查,前端用js获取文件名及大小的方法如下:
File:<input type="file" name="fileupload" οnchange="checkFile(this)"/>
function checkFile(file){ var fielSize = file.files[0].size; var fileName = file.files[0].name; var fileType = fileName.split(".").pop(); if(fielSize >= 5*1024*1024){ $(file).val(""); $("h4").text("您上传的 "+ fileName +" 文件太大(超过 5 M)! 请重新上传..."); return; } if(fileType != "rar" && fileType != "jpg" && fileType != "png" && fileType != "zip"){ $(file).val(""); $("h4").text("您上传的 "+fileName+" 文件格式不合适(rar, jpg, png格式可用)! 请重新上传..."); return; } $("h4").text(""); }
3.2 上传文件时出现 文件名中文乱码 及 文件描述信息中文乱码的解决方法:
3.2.1 文件名中文乱码:request.setCharacterEncoding("UTF-8");
3.2.2 文件描述信息中文乱码:String desc = fileItem.getString("UTF-8"); // fileItem 为 FileItem 对象
3.3 下载文件时,浏览器解析中文乱码:
浏览器只能解析 ISO_8859_1 编码。将文件名由 UTF-8 编码转成 ISO_8859_1 编码,以免浏览器解析乱码
String fileName = new String(fileInfo.getName().getBytes("UTF-8"),"ISO_8859_1");
3.4 bean的一个字段为desc 是 MySQL的一个关键字,sql语句:"SELECT id, NAME, path, file_desc desc FROM files where id=?";不能通过,报 sql语句异常。
应改为:"SELECT id, NAME, path, file_desc AS \"desc\" FROM files where id=?"