文件的上传与下载

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件的上传与下载相关的知识,希望对你有一定的参考价值。

为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用Commons-fileupload组件实现。

 

使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:Commons-fileupload和commons-io。

commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。

 

//如果表单类型为multipart/form-data的话,在servlet中注意就不能采用传统方式获取数据
//String username = request.getParameter("username");这样是获取不到值的
//如果表单类型为multipart/form-data的话,会使用mime协议把输入项按分隔符分隔开来传

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP ‘upload.jsp‘ starting page</title>
  </head>
  <body>
    <form action="${pageContext.request.contextPath }/servlet/UploadServlet2" enctype="multipart/form-data" method="post">
        上传用户:<input type="text" name="username"><br/>
        上传文件1:<input type="file" name="file1"><br/>
        上传文件2:<input type="file" name="file2"><br/>
        <input type="submit" value="上传">
    </form>
  </body>
</html>

 

//处理上传数据
public class UploadServlet2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try{
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            List<FileItem> list = upload.parseRequest(request);
            for(FileItem item : list){
                if(item.isFormField()){
                    //为普通输入项
                    String inputName = item.getFieldName();
                    String inputValue = item.getString();
                    System.out.println(inputName + "="  + inputValue);
                }else{
                    //代表当前处理的item里面封装的是上传文件
                    //C:\Documents and Settings\ThinkPad\桌面\a.txt    a.txt
                    String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1);  
                    InputStream in = item.getInputStream();
                    int len = 0;
                    byte buffer[] = new byte[1024];
                    FileOutputStream out = new FileOutputStream("c:\\" + filename);
                    while((len=in.read(buffer))>0){
                        out.write(buffer, 0, len);
                    }
                    in.close();
                    out.close();
                }
            }
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

 

文件上传与下载要注意的问题:

1.上传文件的中文乱码

  1.1 解决上传文件中有中文名字的乱码

    ServletFileUpload.setHeaderEncoding("UTF-8") ;//设置的编码要与jsp页面使用的编码一致,jsp一般用UTF-8编码、

  1.2 解决普通输入项的乱码(注意,表单类型为multipart/form-data的时候,设置request的编码是无效的)

    FileItem.setString("UTF-8"); //解决乱码

     也可以按照之前的手工解决方式:inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");

2.在处理表单之前,要记得调用:因为如果不是multipart/form-data类型,就没有必要按照上传方式来处理。当然使用上传方式也可以,效率可能会低些。

  ServletFileUpload.isMultipartContent方法判断提交表单的类型,如果该方法返回true,则按上传方式处理,否则按照传统方式处理表单即可。


3.设置解析器缓冲区的大小,以及临时文件的删除

  设置解析器缓冲区的大小 DiskFileItemFactory.setSizeThreshold(1024*1024);

  设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。

 

  public void setRepository(java.io.File repository)
  指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").

 

  临时文件的删除:在程序中处理完上传文件后,一定要记得调用item.delete()方法,以删除临时文件

 

4.在做上传系统时,千万要注意上传文件的保存目录,这个上传文件的保存目录绝对不能让外界直接访问到。一定要放在WEB-INF目录下。否则会存在安全隐患,别人可能会上传一个jsp文件,然后来访问该jsp文件,这样jsp中可能包含危险的代码执行。

5.限制上传文件的类型

  在处理上传文件时,判断上传文件的后缀名是不是允许的

6.限制上传文件的大小

  调用解析器的ServletFileUpload.setFileSizeMax(1024*1024*5);就可以限制上传文件的大小,如果上传文件超出限制,则解析器会抛 

  FileUploadBase.FileSizeLimitExceededException异常,程序员通过是否抓到这个异常,进而就可以给用户友好提示。


7.如何判断空的上传输入项
  String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //""
  if(filename==null || filename.trim().equals("")){
    continue;
  }

8、为避免上传文件的覆盖,程序在保存上传文件时,要为每一个文件生成一个唯一的文件名
  public String generateFileName(String filename){
    return UUID.randomUUID().toString() + "_" + filename;//83434-83u483-934934
  }

9、为避免在一个文件夹下面保存超过1000个文件,影响文件访问性能,程序应该把上传文件打散后存储。
  public String generateSavePath(String path,String filename){
    int hashcode = filename.hashCode(); //121221
    int dir1 = hashcode&15;
    int dir2 = (hashcode>>4)&0xf;

    String savepath = path + File.separator + dir1 + File.separator + dir2;
    File file = new File(savepath);
    if(!file.exists()){
      file.mkdirs();
    }
    return savepath;
  }

10、监听上传进度
  ServletFileUpload upload = new ServletFileUpload(factory);
  upload.setProgressListener(new ProgressListener(){
    public void update(long pBytesRead, long pContentLength, int pItems) {
    System.out.println("当前已解析:" + pBytesRead);
  }
  });

11、在web页面中添加动态上传输入项

 

代码体现如下:

public class UploadServlet3 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        List types = Arrays.asList("jpg","gif","avi","txt");
        
        try{
            DiskFileItemFactory factory = new DiskFileItemFactory();  //解析器缓冲区默认是10k  
            factory.setSizeThreshold(1024*1024);//设置缓冲区大小,上传文件超过该值,将使用临时文件来存储
            factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));//临时文件的存储目录,一定记得最好要关闭
            
            ServletFileUpload upload = new ServletFileUpload(factory);
            upload.setProgressListener(new ProgressListener(){
                public void update(long pBytesRead, long pContentLength, int pItems) {
                    System.out.println("当前已解析:" + pBytesRead);
                }
            });
            
            upload.setFileSizeMax(1024*1024*5);
            if(!upload.isMultipartContent(request)){
                //按照传统方式获取表单数据
                request.getParameter("username");
                return;
            }
            upload.setHeaderEncoding("UTF-8");
            List<FileItem> list = upload.parseRequest(request);
            
            for(FileItem item : list){
                if(item.isFormField()){
                    //为普通输入项
                    String inputName = item.getFieldName();
                    String inputValue = item.getString("UTF-8");
                    //inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");
                    System.out.println(inputName + "="  + inputValue);
                }else{
                    String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1);  //""
                    if(filename==null || filename.trim().equals("")){
                        continue;//结束本次循环,进行下次循环
                    }
                    
                    /*String ext = filename.substring(filename.lastIndexOf(".")+1);
                    if(!types.contains(ext)){
                        request.setAttribute("message", "本系统不支持" + ext + "这种类型");
                        request.getRequestDispatcher("/message.jsp").forward(request, response);
                        return;
                    }*/
                    InputStream in = item.getInputStream();
                    int len = 0;
                    byte buffer[] = new byte[1024];
                    String saveFileName = generateFileName(filename);
                    String savepath = generateSavePath(this.getServletContext().getRealPath("/WEB-INF/upload"),saveFileName);
                    FileOutputStream out = new FileOutputStream(savepath + File.separator + saveFileName);
                    while((len=in.read(buffer))>0){
                        out.write(buffer, 0, len);
                    }
                    in.close();
                    out.close();
                    item.delete();  //删除临时文件    一定要位于关闭流的后面,因为必须关闭流才能删除,否则文件有流与之关联,是删除不掉的。
                }
            }
        }catch (FileUploadBase.FileSizeLimitExceededException e) {
            request.setAttribute("message", "文件大小不能超过5m");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
        request.setAttribute("message", "上传成功!!");
        request.getRequestDispatcher("/message.jsp").forward(request, response);
    }
    
    //
    public String generateSavePath(String path,String filename){
        int hashcode = filename.hashCode();  //121221
        int dir1 = hashcode&15;
        int dir2 = (hashcode>>4)&0xf;
        
        String savepath = path + File.separator + dir1 + File.separator + dir2;
        File file = new File(savepath);
        if(!file.exists()){
            file.mkdirs();
        }
        return savepath;
    }
    
    public String generateFileName(String filename){
        //83434-83u483-934934
        return UUID.randomUUID().toString() + "_" + filename;
    }

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

}

 

 

下载

public class DownLoadServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        //得到要下载的文件名  uuid
        String filename = request.getParameter("filename");
        filename = new String(filename.getBytes("iso8859-1"),"UTF-8");
        
        //找出这个文件  url    c:\\
        String path = this.getServletContext().getRealPath("/WEB-INF/upload") + File.separator + getpath(filename);
        
        File file = new File(path + File.separator + filename);
        if(!file.exists()){
            request.setAttribute("message", "对不起,您要下载的资源已被删除");
            request.getRequestDispatcher("/message.jsp").forward(request, response);
            return;
        }
        
        //得到文件的原始文件名
        String oldname = file.getName().substring(file.getName().indexOf("_")+1);  
        
        //通知浏览器以下载方式打开下面发送的数据
        response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(oldname,"UTF-8"));
        
        FileInputStream in = new FileInputStream(file);
        int len = 0;
        byte buffer[] = new byte[1024];
        OutputStream out = response.getOutputStream();
        while((len=in.read(buffer))>0){
            out.write(buffer, 0, len);
        }
        in.close();
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
    
    public String getpath(String filename){
        int hashcode = filename.hashCode();  //121221
        int dir1 = hashcode&15;
        int dir2 = (hashcode>>4)&0xf;

        return dir1 + File.separator + dir2;  //   3/5
    }

}

 

以上是关于文件的上传与下载的主要内容,如果未能解决你的问题,请参考以下文章

文件的上传与下载

性能工具之 JMeter 上传与下载脚本编写

springboot项目关于文件的上传与下载

文件上传与下载

Android文件上传与下载

上传与下载文件