文件上传和下载

文件上传简介:

 1.文件上传浏览器端         

1.method=post 只有post才可以携带大数据
2.必须使用<input type='file' name='f'>要有name属性
3.encType="multipart/form-data"
View Code

 2.文件上传服务器端:request对象用于获取请求信息。有一个方法 getInputStream(); 可以获取一个字节输入流,通过这个流,可以读取到所有的请求正文信息。

  2.1 upload1.jsp

html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
  </head>
  <body>
    <form action="${pageContext.request.contextPath}/upload1" method="post" encType="multipart/form-data">
        <input type="text" name="content"><br>
        <input type="file" name="f"><br>
        <input type="submit" value="上传">
    </form>
  </body>
</html>
View Code

    页面示例:

 2.2 Upload1Servlet.java  

import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Upload1Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //通过request获取一个字节输入流,将所有的请求正文信息读取到,打印到控制台。
        InputStream is=request.getInputStream();
        
        byte[] b=new byte[1024];
        int len=-1;
        
        while((len=is.read(b))!=-1){
            
            System.out.print(new String(b,0,len));
            
        }
        is.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
View Code

 2.3 测试结果

  3.文件上传原理:浏览器端注意上述三件事,在服务器端通过流将数据读取到,在对数据进行解析.将上传文件内容得到,保存在服务器端,就完成了文件上传。在实际开发中,不需要我们进行数据解析,完成文件上传。因为我们会使用文件上传的工具(commons-fileupload),它们已经封装好的,提供API,只要调用它们的API就可以完成文件上传操作。 

文件上传工具:commons-fileupload

1.核心类介绍

commons-fileupload-1.2.1.jar  文件上传commons-io-1.4.jar 它是提供的io工具.

它有三个核心:1.DiskFileItmeFactory类     2.ServletFileUpload类    3.FilteItem

1.DiskFileItemFactory作用:可以设置缓存大小以及临时文件保存位置.默认缓存大小是  10240(10k).    临时文件默认存储在系统的临时文件目录下.(可以在环境变量中查看)
new DiskFileItemFactory();
缓存大小与临时文件存储位置使用默认的.
                
DiskFileItemFactory(int sizeThreshold, File repository) 
sizeThreshold :缓存大小    repository:临时文件存储位置
                
注意,对于无参数构造,也可以设置缓存大小以及临时文件存储位置.    
setSizeThreshold(int sizeThreshold)
setRepository(File repository)

2.ServletFileUpload
ServletFileUpload upload=new ServletFileUpload(factory);
 创建一个上传工具,指定使用缓存区与临时文件存储位置.
            
List<FileItem> items=upload.parseRequest(request);
它是用于解析request对象,得到所有上传项.每一个FileItem就相当于一个上传项.
                
boolean flag=upload.isMultipartContent(request);
用于判断是否是上传.    可以简单理解,就是判断encType="multipart/form-data";

设置上传文件大小
void setFileSizeMax(long fileSizeMax) 设置单个文件上传大小 
void  setSizeMax(long sizeMax) 设置总文件上传大小 

解决上传文件中文名称乱码
setHeaderEncoding("utf-8");
注意:如果使用reqeust.setCharacterEncoding("utf-8")也可以,但不建议使用。

3.FileItem
3.1.isFormField
用于判断是否是上传组件.如果是<input type="file">返回的就是false,否则返回true.
                
3.2.getFieldName();
返回值String,得到组件名称  <input name="">
3.3.getName();
返回值是String,得到的是上传文件的名称.    注意:浏览器不同,它们得到的效果不一样。
1.包含全路径名称  例如: C:\Users\Administrator\Desktop\a.txt
2.只包含上传文件名称 例如:a.txt
                    
3.4.getString();
这个方法可以获取非上传组件的内容,相当于  getParameter方法作用。
                
问题:如果信息是中文,会出现乱码,解决方案  getString("utf-8");
                
如果是上传组件,上传的文件是文本文件,可以获取到文件文件的内容。
但是如果不是文件文件,例如:是一张图片,这样获取合适吗?
                    
3.5.获取上传文件的内容,保存到服务器端.
item.getInputStream();它是用于读取上传文件内容的输入流.
使用文件复制操作就可以完成文件上传。
3.6.删除临时文件 item.delete();

总结:关于文件上传时的乱码问题:
1.上传文件名称乱码
ServletFileUpload.setHeaderEncoding("utf-8");                
2.非上传组件内容乱码
FileItem.getString("utf-8");
3.思考:上传文件信息是否会乱码,需要解决吗?
不需要解决,因为我们在上传时,使用的字节流来进行复制。
View Code

 2.文件上传注意事项

1.上传文件在服务器端保存位置问题
   1.1.保存在可以被浏览器直接访问的位置
      例如:商城的商品图片
      保存在工程的WebRoot下的路径(不包含META-INF以及WEB-INF目录及其子目录)
                
   1.2.保存在不能被浏览器直接访问的位置
    例如:付费的视频。        
        1).工程中META-INF  WEB-INF目录及其子目录
    2).不在工程中的服务器的磁盘目录下.
                
    
2.上传文件在同一个目录重名问题     
   在开发中解决这个问题,可以给上传文件起随机名称。
   1).使用毫秒值            2).使用uuid
        

3.同一目录下文件过多只需要分目录就可以.
   1) 按照上传时间进行目录分离 (周、月 )
   2) 按照上传用户进行目录分离 ----- 为每个用户建立单独目录 
   3) 按照固定数量进行目录分离 ------ 假设每个目录只能存放3000个文件 ,每当一个目录存满3000个文件后,创建一个新的目录
  4)按照文件名的hashcode进行目录分离.
                    
public static String generateRandomDir(String uuidFileName) {
    // 获得唯一文件名的hashcode
    int hashcode = uuidFileName.hashCode();
    // 获得一级目录
    int d1 = hashcode & 0xf;       
    // 获得二级目录
    int d2 = (hashcode >>> 4) & 0xf;
    return "/" + d2 + "/" + d1;// 共有256目录l
                }
View Code

3.多文件上传实例

  3.1 上传页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    
    <title>多文件上传</title>
    
    <script type="text/javascript">
        
    function addFile(){
        var div=document.getElementById("content");
        
        div.innerHTML+="<div><input type='file' name='f'><input type='button' value='remove file' onclick='removeFile(this)'></div>";
    }
    
    function removeFile(btn){
        
        document.getElementById("content").removeChild(btn.parentNode);
        
    }
    </script>
    
  </head>
  
  <body>
    
    <input type="button" value="add File" onclick="addFile();">
    <br>
    <br>
    <form action="${pageContext.request.contextPath}/upload4" method="post" encType="multipart/form-data">
        <input type="file" name="f"><br>
        <div id="content">
        </div>
        <input type="submit" value="上传">
    </form>
  </body>
</html>
View Code

        页面示例

   

 3.2 Upload4Servlet.java

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;

import cn.itcast.utils.FileUploadUtils;

public class Upload4Servlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        response.setContentType("text/html;charset=utf-8");

        // 1.创建 DiskFileItemFactory
        File file = new File(this.getServletContext().getRealPath("/temp"));// 获取temp目录部署到tomcat后的绝对磁盘路径
        DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 100, file); // 使用默认的.

        // 2.创建ServletFileUpload
        ServletFileUpload upload = new ServletFileUpload(factory);
        boolean flag = upload.isMultipartContent(request); // 用于判断是否是上传操作.
        if (flag) {
            // 解决上传文件名称中文乱码
            upload.setHeaderEncoding("utf-8");
            // 设置上传文件大小
            // upload.setSizeMax(1024 * 1024 * 10);// 总大小为10m
            try {
                List<FileItem> items = upload.parseRequest(request);// 解决request,得到所有的上传项FileItem
                // 3.得到所有上传项
                for (FileItem item : items) {
                    
                    if (!item.isFormField()) {
                        // item.isFormField()得到了<input type="text" name="content">这样的组件
                           /*String fieldName = item.getFieldName();
                             String name = item.getName();
                             String msg = item.getString();
                           */
                        
                        // !item.isFormField()就是<input type="file"> 这样的上传组件
                        String name = item.getName(); // 上传文件名称
                        // 得到上传文件真实名称
                        String filename = FileUploadUtils.getRealName(name);
                        // 得到随机名称
                        String uuidname = FileUploadUtils.getUUIDFileName(filename);
                        // 得到随机目录
                        String randomDirectory = FileUploadUtils.getRandomDirectory(filename);
                        // 注意:随机目录可能不存在,需要创建.
                        String parentPath = this.getServletContext().getRealPath("/upload");
                        File rd = new File(parentPath, randomDirectory);
                        if (!rd.exists()) {
                            rd.mkdirs();
                        }

                        IOUtils.copy(item.getInputStream(),new FileOutputStream(new File(rd, uuidname)));

                        // 删除临时文件
                        item.delete();
                    }
                }

            } catch (FileUploadException e) {
                // e.printStackTrace();
                response.getWriter().write(e.getMessage());
                return;
            }
        } else {
              response.getWriter().write("不是上传操作");
              return;
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

}
View Code

3.3 上传辅助类 FileUploadUtils.java 

import java.io.File;
import java.util.UUID;

public class FileUploadUtils {

    // 得到上传文件真实名称 c:\a.txt a.txt
    public static String getRealName(String filename) {

        int index = filename.lastIndexOf("\\") + 1;

        return filename.substring(index);

    }

    // 获取随机名称 a.txt
    public static String getUUIDFileName(String filename) {
        int index = filename.lastIndexOf(".");
        if (index != -1) {

            return UUID.randomUUID() + filename.substring(index);
        } else {
            return UUID.randomUUID().toString();
        }
    }

    // 目录分离算法
    public static String getRandomDirectory(String filename) {

        // int hashcode = filename.hashCode();
        //
        // // System.out.println(hashcode);
        //
        // // int类型数据在内存中占32位。转换成16进制数,就得到8个16进制数
        // String hex = Integer.toHexString(hashcode);
        //
        // // System.out.println(hex); // 056d9363
        //
        // return "/" + hex.charAt(0) + "/" + hex.charAt(1);

        int hashcode = filename.hashCode();

        System.out.println(Integer.toBinaryString(hashcode));

        int a = hashcode & 0xf;

        hashcode = hashcode >>> 4;

        int b = hashcode & 0xf;

        return "/" + a + "/" + b;

    }

}
View Code

3.4 上传结果 

文件下载:

文件下载的方式:

1.超连接下载

download1.jsp
        <a href='${pageContext.request.contextPath}/upload/a.bmp'>a.bmp</a><br>
        <a href='${pageContext.request.contextPath}/upload/a.doc'>a.doc</a><br>
        <a href='${pageContext.request.contextPath}/upload/a.txt'>a.txt</a><br>
        <a href='${pageContext.request.contextPath}/upload/tk.mp3'>tk.mp3</a><br>

        注意:如果文件可以直接被浏览器解析,那么会在浏览器中直接打开,不能被浏览器直接解析,就是下载操作。
             直接打开的要想下载 ,右键另存为。

        超连接下载,要求下载 的资源,必须是可以直接被浏览器直接访问的。
            
        客户端访问服务器静态资源文件时,静态资源文件是通过 缺省Servlet返回的,
        在tomcat配置文件conf/web.xml 找到 --- org.apache.catalina.servlets.DefaultServlet
View Code

2.服务器端通过流下载(服务器端编程) 

1.创建download2.jsp
            <a href='${pageContext.request.contextPath}/download?filename=a.bmp'>a.bmp</a><br>
            <a href='${pageContext.request.contextPath}/download?filename=a.doc'>a.doc</a><br>
            <a href='${pageContext.request.contextPath}/download?filename=a.txt'>a.txt</a><br>
            <a href='${pageContext.request.contextPath}/download?filename=tk.mp3'>tk.mp3</a><br>
            
        2.创建DownloadServlet
            // 1.得到要下载 的文件名称
            String filename = request.getParameter("filename");
            
            //2.判断文件是否存在
            File file = new File("d:/upload/" + filename);
            if (file.exists())
        
            //3.进行下载 
                原理:就是通过response获取一个输出流,将要下载的文件内容写回到浏览器端就可以了.
                
        注意:要想通过编程的方式,实现文件下载,
            1.要设置mimetype类型
                resposne.setContextType(String mimeType);
                
                问题:怎样可以得到要下载文件的mimeType类型?
                    ServletContext.getMimeType(String filename);
                    
                如果设置了mimeType,浏览器能解析的就直接展示了,不能解析的,直接下载.
                
            2.设置一个响应头,设置后的效果,就是无论返回的是否可以被浏览器解析,就是下载 。
                response.setHeader("content-disposition","attachment;filename=下载文件名称");
                
        总结:服务器端编程下载:
            1.将下载的文件通过resposne.getOutputStream()流写回到浏览器端。
            2.设置mimeType  response.setContentType(getServletContext.getMimeType(String filename));
            3.设置响应头,目的是永远是下载操作
                response.setHeader("content-disposition","attachment;filename=下载文件名称");
View Code

 3.文件下载时的乱码问题

1.关于下载时中文名称资源查找不到
                原因:<a href='${pageContext.request.contextPath}/download?filename=天空.mp3'>天空.mp3</a>
                  这是get请求。
                  
                  在服务器端:
                  String filename = request.getParameter("filename");
                  
                 解决: new String(filename.getBytes("iso8859-1"),"utf-8"); 
                 
            2.下载文件显示时的中文乱码问题
                    response.setHeader("content-disposition", "attachment;filename="+filename);
                    
                    IE:要求filename必须是utf-8码
                    firefox:要求filename必须是base64编码.
                    
                    问题:怎样判断浏览器?
                        String agent=request.getHeader("user-agent");
                    
                        if (agent.contains("MSIE")) {
                            // IE浏览器
                            filename = URLEncoder.encode(filename, "utf-8");
                            
                        } else if (agent.contains("Firefox")) {
                            // 火狐浏览器
                            BASE64Encoder base64Encoder = new BASE64Encoder();
                            filename = "=?utf-8?B?"
                                    + base64Encoder.encode(filename.getBytes("utf-8"))
                                    + "?=";
                        }else {
                            // 其它浏览器
                            filename = URLEncoder.encode(filename, "utf-8");
                        }
View Code

 附件:文件上传和下载源码 密码:dpsu;

 

posted @ 2017-06-22 14:12  ABO-阿博  阅读(448)  评论(0编辑  收藏  举报