小案例:文件上传 和下载

JavaWeb之文件上传  和 文件下载

2016年11月25日19:54:35(javaweb 学习日记)
导读{
}

【1】文件上传

【1.1】表单页面设置

 Web应用中,由于大多数文件的上传都是通过表单的形式提交给服务器的,因此,要想在程序中实现文件上传的功能,首先得创建一个用于提交上传文件的表单页面。


为了使Servlet程序可以获取到上传文件的数据,需要将表单页面的method属性设置为post方式enctype属性设置为multipart/form-data”类型,添加文件的input标签类型设置为file类型

【1.2】Servlet编写

【1.2.1】基本知识点
    由于 请求正文 多个部分 组成 ,解析这部分内容比较麻烦。 实现文件的上传主要通过  Apache 组织提供了一个开源组件 Commons-FileUpload 来实现
需要用到 
/commons-fileupload-1.2.jar  实现上传  下载地址   http://cn.jarfire.org/commons.fileupload.html
/commons-io-1.4.jar   实现下载                                                   http://cn.jarfire.org/commons.io.html

    DiskFileItemFactory类用于将请求消息实体中的每一个文件封装成单独的FileItem对象。如果上传的文件比较小,将直接保存在内存中,如果上传的文件比较大,则会以临时文件的形式,保存在磁盘的临时文件夹中。默认情况下,文件保存在内存还是硬盘临时文件夹的临界值是10240,即10kb

 
     ServletFileUpload 类是 Apache 组件处理文件上传的核心高级类,通过使用 parseRequest ( HttpServletRequest 方法可以将 HTML 中每个表单提交的数据封装成一个 FileItem 对象,然后以 List 列表的形式返回。 ServletFileUpload 类的两个构造方法如下所示:

ServletFileUpload 方法声明

方法声明

功能描述

setSizeMax(long sizeMax)

getSizeMax()

setSizeMax()方法继承自FileUploadBase类,用于设置请求消息实体内容(即所有上传数据)的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其中,参数sizeMax是以字节为单位。对应的getSizeMax()方法用于读取请求消息实体内容所允许的最大值

setFileSizeMax(long fileSizeMax)

setFileSizeMax()方法继承自FileUploadBase类,用于设置单个上传文件的最大尺寸限制,以防止客户端恶意上传超大文件来浪费服务器端的存储空间。其中,参数fileSizeMax是以字节为单位。对应的geFileSizeMax()方法用于获取单个上传文件所允许的最大值。

parseRequest(javax.servlet.http.

HttpServletRequest req)

parseRequest()方法是ServletFileUpload类的重要方法,它是对HTTP请求消息体内容进行解析的入口。它解析出FORM表单中的每个字段的数据,并将它们分别包装成独立的FileItem对象,然后将这些FileItem对象加入进一个List类型的集合对象中返回。

getItemIterator(HttpServletRequest request)

getItemIterator()方法和parseRequest()方法基本相同。但是getItemIterator()方法返回的是一个迭代器,该迭代器中保存的不是FileItem对象,而是FileItemStream对象,如果你希望进一步提高性能,你可以采用getItemIterator()方法,直接获得每一个文件项的数据输入流,做底层处理;如果性能不是问题,你希望代码简单,则采用parseRequest()方法即可。

isMultipartContent(HttpServletRequestreq)

isMultipartContent()方法用于判断请求消息中的内容是否是“multipart/form-data”类型,如果是,则返回true,否则返回false。需要注意的是,isMultipartContent()方法是一个静态方法,不用创建ServletFileUpload类的实例对象即可被调用。

getFileItemFactory()

setFileItemFactory(FileItemFactory factory)

这两个方法继承自FileUpload类,分别用于读取和设置fileItemFactory属性。

setHeaderEncoding(String encoding)getHeaderEncoding()

这两个方法继承自FileUploadBase类,用于设置和读取字符编码。需要注意的是,如果没有使用setHeaderEncoding()设置字符编码,则getHeaderEncoding()方法返回null,上传组件会采用HttpServletRequest设置的字符编码。但是,如果HttpServletRequest的字符编码也为null,这时,上传组件将采用系统默认的字符编码。获取系统默认字符编码的方式如:System.getProperty("file.encoding"));

可以看下 上传文件的流程


 
从这个图上可以看出我们使用的一些方法和接口
其中我们处理上传文件的时候,通常我们都有上传用户,谁上传的文件。\

我们可以在表单中设置一个 name= username   type=text   的参数
   
   
  1. //如果当前表单项为普通文本字段
  1. if(fileItem.isFormField()){
  1. //获取当前表单项的字段名称
  1. String filedName = fileItem.getFieldName();
  1. //如果表单字段名为username
  1. if(filedName.equals("username")){
  1. //输出当前用户输入的值,用UTF-8解码解决中文乱码
  1. response.getWriter().print("用户名:"+fileItem.getString("UTF-8")+"<br>");
  1. }
这样就拿到我们的上传用户
 
  1. //获取上传的文件名(原始文件名)
  2. String name = fileItem.getName();
FileItem 中的方法声明

方法声明

功能描述

boolean isFormField()

isFormField()方法用于判断FileItem类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回true,否则返回false

String getName()

getName()方法用于获得文件上传字段中的文件名。如果FileItem类对象对应的是普通表单字段,getName()方法将返回null,否则,只要浏览器将文件的字段信息传递给服务器,getName()方法就会返回一个字符串类型的结果,如:“C:\Sunset.jpg”。

String getFieldName()

getFieldName()方法用于获得表单字段元素描述头的name属性值,也是表单标签name属性的值。例如“name=file1”中的“file1”。

void write(File file)

write()方法用于将FileItem对象中保存的主体内容保存到某个指定的文件中。如果FileItem对象中的主体内容是保存在某个临时文件中,那么该方法顺利完成后,临时文件有可能会被清除。另外,该方法也可将普通表单字段内容写入到一个文件中,但它主要用于将上传的文件内容保存到本地文件系统中。

String getString()

getString()方法用于将FileItem对象中保存的数据流内容以一个字符串返回,它有两个重载的定义形式:

public String getString()

public String getString(java.lang.String encoding)

String getContentType()

getContentType()方法用于获得上传文件的类型,即表单字段元素描述头属性“Content-Type”的值,如“image/jpeg”。如果FileItem类对象对应的是普通表单字段,该方法将返回null

boolean isInMemory()

isInMemory()方法用来判断FileItem对象封装的数据内容是存储在内存中,还是存储在临时文件中,如果存储在内存中则返回true,否则返回false

void delete()

delete()方法用来清空FileItem类对象中存放的主体内容,如果主体内容被保存在临时文件中,delete()方法将删除该临时文件。需要注意的是,尽管FileItem对象被垃圾收集器收集时会自动清除临时文件,但应该及时调用delete()方法清除临时文件,从而释放系统存储资源,以防系统出现异常,导致临时文件被永久的保存在硬盘中。

InputStream getInputStream()

getInputStream()方法以流的形式返回上传文件的数据内容。

long getSize()

getSize()方法返回该上传文件的大小(以字节为单位)。

【1.2.2】代码部分
(1)servlet.java
   
   
  1. package cn.edu.aynu.rjxy.servlet;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.util.List;
  5. import java.util.UUID;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import org.apache.commons.fileupload.FileItem;
  11. import org.apache.commons.fileupload.FileUploadBase;
  12. import org.apache.commons.fileupload.FileUploadException;
  13. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  14. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  15. public class UploadServlet extends HttpServlet {
  16. public void doPost(HttpServletRequest request, HttpServletResponse response)
  17. throws ServletException, IOException {
  18. //处理响应的中文乱码
  19. response.setContentType("text/html;charset=UTF-8");
  20. //创建工厂对象
  21. DiskFileItemFactory factory = new DiskFileItemFactory();
  22. //使用工厂对象创建ServletFileUpload对象(解析器)
  23. ServletFileUpload fileUpload = new ServletFileUpload(factory);
  24. //解决文件名中文乱码
  25. fileUpload.setHeaderEncoding("UTF-8");
  26. //设置上传的单个文件的大小,不能超过1024kB(1MB)
  27. fileUpload.setFileSizeMax(1024*1024);
  28. try {
  29. //使用解析器解析request,得到包含fileItem对象的链表
  30. List<FileItem> list = fileUpload.parseRequest(request);
  31. //遍历list,得到每一个fileItem(也就是每一个表单项)
  32. for(FileItem fileItem:list){
  33. //如果当前表单项为普通文本字段
  34. if(fileItem.isFormField()){
  35. //获取当前表单项的字段名称
  36. String filedName = fileItem.getFieldName();
  37. //如果表单字段名为username
  38. if(filedName.equals("username")){
  39. //输出当前用户输入的值,用UTF-8解码解决中文乱码
  40. response.getWriter().print("用户名:"+fileItem.getString("UTF-8")+"<br>");
  41. }
  42. }else{//文件上传字段
  43. //获取上传的文件名(原始文件名)
  44. String name = fileItem.getName();
  1. //我们先得到最后一个 \ 字符的位置,那么需要用到转义字符 \\ 转义就是 \
  2. int lastIndexOf = name.lastIndexOf("\\");
  3. //切割字符串
  4. String substring = name.substring(lastIndexOf + 1);
  5. name = substring;
  1. //如果上传的文件名为空(没有指定上传文件)
  2. if(name==null||name.isEmpty()){
  3. continue;
  4. }
  5. //获取上传文件保存的真实路径(绝对路径),上传的文件保存的目录
  6. String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");
  7. //使用UUID对上传的文件进行重命名,保证文件名是唯一的
  8. String uuid = UUID.randomUUID().toString();
  9. //新的文件名,格式:uuid_原始文件名
  10. String filename = uuid+"_"+name;
  11. /**
  12. * 在uploads目录下创建多个文件夹,让每个文件夹存放少量文件
  13. int hashcode = name.hashCode();
  14. //获取hashcode的低4位,并转换成16进制字符串
  15. String dir1 = Integer.toHexString(hashcode & 0XF);
  16. //获取hashcode的5~8位,并转换成16进制字符串
  17. String dir2 = Integer.toHexString(hashcode>>>4 & 0XF);
  18. //与文件目录合并构成完整路径
  19. savepath = savepath + "/" + dir1 +"/" +dir2;
  20. //如果不存在,就创建文件夹
  21. new File(savepath).mkdirs();
  22. */
  23. //通过文件保存目录和文件名创建File对象
  24. File file = new File(savepath, filename);
  25. //将上传文件保存到指定位置,上传结束
  26. fileItem.write(file);
  27. //给客户端输出一些提示
  28. response.getWriter().print("上传文件名:"+name+"<br>");
  29. response.getWriter().print("上传文件的大小:"+fileItem.getSize()+"<br>");
  30. response.getWriter().print("上传文件的类型:"+fileItem.getContentType());
  31. }
  32. }
  33. } catch (Exception e) {
  34. //判断抛出的异常是否为超出了单个文件大小的上限
  35. if(e instanceof FileUploadBase.FileSizeLimitExceededException){
  36. //将错误消息放到request域中
  37. request.setAttribute("msg", "上传失败,上传文件超过了1M字节!");
  38. //通过请求转发将消息带到upload.jsp上
  39. request.getRequestDispatcher("/upload.jsp").forward(request, response);
  40. return;
  41. }
  42. // TODO Auto-generated catch block
  43. e.printStackTrace();
  44. }
  45. }
  46. }
index.jsp
    
    
  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  2. <%
  3. String path = request.getContextPath();
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5. %>
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  7. <html>
  8. <head>
  9. <base href="<%=basePath%>">
  10. <title>My JSP 'index.jsp' starting page</title>
  11. <meta http-equiv="pragma" content="no-cache">
  12. <meta http-equiv="cache-control" content="no-cache">
  13. <meta http-equiv="expires" content="0">
  14. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  15. <meta http-equiv="description" content="This is my page">
  16. <!--
  17. <link rel="stylesheet" type="text/css" href="styles.css">
  18. -->
  19. </head>
  20. <body> <!--文件上传必须使用Post 提交,设置enctype(编码类型)="multipart/form-data" -->
  21. <form action="/demo/fileUploadServlet" method="post" enctype="multipart/form-data" >
  22. 上传者:<input type="text" name="username" /><br>
  23. 上传文件:<input type="file" name="name" /><br>
  24. <input type="submit" value="上传" />
  25. </form>
  26. </body>
  27. </html>

注意(报错情况分析):
针对不同的浏览器上传文件可能会出现这样的一个错误: 文件名,目录名,或卷标语法不正确
  1. //获取上传的文件名(原始文件名)
  2. String name = fileItem.getName();
我们发现打印的这个信息就是 我们上传的文件名,但是我们的文件名应该只是  demo.txt 
所以我们要切割 name 从其中拿到 demo.txt 字段
解决方法:
   
   
  1.            
  2. String name = fileItem.getName();
  3. //我们先得到最后一个 \ 字符的位置,那么需要用到转义字符 \\ 转义就是 \
  4. int lastIndexOf = name.lastIndexOf("\\");
  5. //切割字符串
  6. String substring = name.substring(lastIndexOf + 1);
  7. name = substring;
那么我们现在拿到的 name 就是  demo.txt 字段
这样方式也不会影响正常情况,拿到的name 就是 demo.txt   

【2】web 文件下载

实现文件下载功能比较简单,直接使用Servlet输入/输出流实现即可。实现文件的下载,不仅需要指定文件的路径,还需要在HTTP协议中设置两个响应消息头:

 

注意点:
  1. // 设置下载方式 dispositio 处理
  2. response.setHeader("Content-Disposition", "attachment;filename="
  3. + URLEncoder.encode(filename, "UTF-8"));
这个需要设置正确,否则就不会出现 有下载框的 下载方式
不写这个 ,就是直接在页面显示打开。
——————————————————————————————————————————————————————————————————————————————————————
主要使用就是     IOUtils.copy(fileInputStream, response.getOutputStream());  
 把我们拿到的文件输入流 转化为 输出流 返回出去
   
   
  1. package cn.edu.aynu.sushe;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.io.PrintWriter;
  5. import java.net.URLEncoder;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.http.HttpServlet;
  8. import javax.servlet.http.HttpServletRequest;
  9. import javax.servlet.http.HttpServletResponse;
  10. import org.apache.commons.io.IOUtils;
  11. public class DowloadServlet extends HttpServlet {
  12. public void doGet(HttpServletRequest request, HttpServletResponse response)
  13. throws ServletException, IOException {
  14. // 文件下载
  15. // 拿到我们的参数名
  16. String filename = request.getParameter("filename");
  17. // 处理一下文件名的中文乱码
  18. filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");
  19. // 拿到文件的真实目录(绝对路径)
  20. String path = this.getServletContext().getRealPath(
  21. "/WEB-INF/downloads/" + filename);
  22. // 判断文件属于什么类型
  23. String mimeType = this.getServletContext().getMimeType(filename);
  24. // 设置内容的类型
  25. response.setContentType(mimeType);
  26. // 设置下载方式 dispositio 处理
  27. response.setHeader("Content-Disposition", "attachment;filename="
  28. + URLEncoder.encode(filename, "UTF-8"));
  29. // 使用文件输入流 读取数据
  30. FileInputStream fileInputStream = new FileInputStream(path);
  31. // 使用输出流将文件保存到客户端
  32. // 使用 commons-io 流中的
  33. IOUtils.copy(fileInputStream, response.getOutputStream());
  34. fileInputStream.close();
  35. }
  36. public void doPost(HttpServletRequest request, HttpServletResponse response)
  37. throws ServletException, IOException {
  38. this.doGet(request, response);
  39. }
  40. }
其中可能会出现的问题 :(文件名、目录名或卷标语法不正确)
 
出现问题是因为对中文乱码部分处理了两遍,原因就是在 tomcat的配置目录中的 server.xml ()  (  tomcat/conf/server.xml  )
中对tomcat 端口号设置中多写了一个 URIEncoding="UTF-8"   然后我们在代码里也进行了中文乱码的处理。

设置这个参数就默认在通过get 方式的访问的时候,处理中文乱码
解决方法:
删除这个参数   或者 删除代码中的处理中文乱码的部分

Demo 演示:https://github.com/gengzi/files.git






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值