zoukankan      html  css  js  c++  java
  • 文件上传和下载

    文件上传简介:

     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:UsersAdministratorDesktopa.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;

  • 相关阅读:
    Windows下编译TensorFlow1.3 C++ library及创建一个简单的TensorFlow C++程序
    flask自动重启与配置文件导入,路由重定向(8)
    go(4)字符串的应用
    go(3)变量的应用与数据类型
    go(2)输出,转义,字符接收
    go(1)
    tornado集成wtforms
    HTML5笔记007
    HTML5笔记006
    HTML5笔记005
  • 原文地址:https://www.cnblogs.com/java-oracle/p/7064676.html
Copyright © 2011-2022 走看看