Java web文件上传下载


[版权申明:本文系做者原创,转载请注明出处]
文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666
做者:朱培 ID:sdksdk0 邮箱: zhupei@tianfang1314.cn javascript


本文主要从javaweb上传文件到服务器中,而且在服务器端进行数据文件存储,主要分享了文件上传原理、使用第三方开源工具进行上传以及一些文件上传时须要注意的地方,文件的优化处理,还有简易分享了从咱们刚才上传进去的文件进行下载。须要掌握基本的开发流程,底层实现原理等。php

1、文件上传原理

  • 提供form表单,method必须是post
  • form表单的enctype必须是multipart/form-data
  • 提供input type=”file”

Enctype属性

告知服务器请求正文的MIME类型。
这里写图片描述
application/x-www-form-urlencoded(默认):
正文:name=aa&password=123
服务器获取数据:request.getParameter(“name”);html

文件上传原理:

解析请求正文的每部分的内容。java

基于html form表单上传的数据都是以相似—————————–7da3c8e180752{0x130x10}这样的分割符来标记一块数据的起止。
这里写图片描述git

文件上传的Content-Type为multipart/form-data; boundary=—-WebKitFormBoundaryhQslmBE7nbTLTJzD,而普通的form表单的Content-Type为application/x-www-form-urlencoded。所以,咱们能够利用HttpServletRequest的request.getHeaderNames()方法和request.getHeaders(headName)方法获得请求头Headers中的Content-Type数据,而后根据Content-Type数据中是否包含multipart/form-data来区分请求是否为文件上传请求。其中boundary为文件数据的分隔符,用于区分上传多个文件。github

2、使用第三方工具实现文件上传

fileupload组件工做流程:

这里写图片描述

开发步骤

导入commons-fileupload.jar、commons-io.jar包。
一、界面
咱们就是须要一个form表单,为其添加enctype属性和post方法:web

<form action="${pageContext.request.contextPath}/servlet/UploadServlet2"  method="post"  enctype="multipart/form-data" >
        姓名: <input  type="text"  name="name" /><br />
        照片: <input  type="file"  name="photo" /><br />

            <input type="submit"  value="提交" />
    </form>

二、逻辑处理
咱们在一个servlet中进行处理:sql

request.setCharacterEncoding("UTF-8");
        //判断用户的请求内容是否是multipart/form-data
        boolean isMultipart=ServletFileUpload.isMultipartContent(request);
        if(!isMultipart){
            throw new RuntimeException("error!");
        }

//建立DiskFileItemFactory对象
DiskFileItemFactory factory=new DiskFileItemFactory();缓存

//建立核心解析类ServlertFileUpload
        ServletFileUpload  sfu=new ServletFileUpload(factory);

        //解析请求对象
        List<FileItem> items=new ArrayList<FileItem>(0);

        try {
            items=sfu.parseRequest(request);
        } catch (FileUploadException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        for(FileItem item:items){
            if(item.isFormField()){
                processFormField(item);
            }else{
                processUploadField(item);
            }
        }
        response.getWriter().write("sucess!");

接下来就是分为两种状况了,一种是对普通的表单元素进行处理,另外一种是对文件类的数据进行处理,对于第一种状况的话就比较简单,咱们直接获取名字就能够了,基本上不用过多的处理。服务器

private void processFormField(FileItem item) {
        String fieldName=item.getFieldName();
        String fieldValue=item.getString();
        System.out.println(fieldValue+"="+fieldName);

    }

对于第二种状况,须要咱们直接对上传文件进行一系列的处理了,咱们首先须要在服务器上找一个存放文件的地方,而后截取上传的文件名、构建输出流、关闭流等操做。

private void processUploadField(FileItem item) {

        try {
            InputStream in=item.getInputStream();
            String filename=item.getName();

            //在服务器上找一个存放文件的地方
            String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");
            File storeDirectory=new File(storeDirectoryRealPath);

            if(!storeDirectory.exists()){
                storeDirectory.mkdirs();
            }

            //截取上传的文件名
            //filename=filename.substring(filename.lastIndexOf(File.separator)+1);

            if(filename!=null){
                filename=FilenameUtils.getName(filename);
            }

            String guidFilename=GUIDUtil.generateGUID()+"_"+filename;


            //按日期来区分存储目录
        // String childDirectory=makeChileDirectory(storeDirectory);

            String childDirectory=makeChildDirectory(storeDirectory,guidFilename);

            //构建输出流
            OutputStream  out=new FileOutputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));

            int len = -1;
            byte buf[] = new byte[1024];
            while((len=in.read(buf))!=-1){
                out.write(buf, 0, len);
            }
            in.close();
            out.close();


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3、文件上传优化处理

一、把保存的文件放在用户没法直接访问到的地方:例如放在:在WEB-INF/files目录中。

String storeDirectoryRealPath=getServletContext().getRealPath("/WEB-INF/files");

二、让文件名惟一。

String guidFilename=GUIDUtil.generateGUID()+"_"+filename;
            //构建输出流
            OutputStream  out=new FileOutputStream(new File(storeDirectory,guidFilename));

三、避免同一个文件夹中的文件过多。
3.1按照日期进行存储。

String childDirectory=makeChileDirectory(storeDirectory);

    private String makeChileDirectory(File storeDirectory) {
        Date now=new Date();
        DateFormat df=new SimpleDateFormat("yyyy-MM-dd");
        String sdate=df.format(now);
        File f=new File(storeDirectory,sdate);
        if(!f.exists()){
            f.mkdirs();
        }
        return sdate;
    }

3.2用文件名的hashCode计算须要进行存储的目录,二级目录。

private String makeChildDirectory(File storeDirectory, String guidFilename) {
        int hashCode = guidFilename.hashCode();
        int dir1 = hashCode&0xf;// 0~15
        int dir2 = (hashCode&0xf0)>>4;//0~15

        String s = dir1+File.separator+dir2;

        File f = new File(storeDirectory,s);
        if(!f.exists()){
            f.mkdirs();
        }
        return s;
    }

四、限制文件的大小。web方式不适合上传大的文件。
4.1单个文件大小:

ServletFileUpload  sfu=new ServletFileUpload(factory);
        sfu.setFileSizeMax(4*1024*1024);//限制不超过4M

4.2总文件大小:多文件上传

ServletFileUpload  sfu=new ServletFileUpload(factory);
    sfu.setSizeMax(8*1024*1024);//总文件大小

五、限制文件的上传类型。
5.1经过文件扩展名来进行限制。

 String extensionName=FilenameUtils.getExtension(filename);

5.2经过文件MIME类型来限制。

 String mimeType=item.getContentType();

六、空文件上传解决方案。
判断文件名是否为空,当文件名为空时return。

七、临时文件
DiskFileItemFactory的做用是产生FileItem对象。其内部有一个缓存,默认大写拾10kb,若是上传文件超过10kb,则用磁盘做为缓存。存放缓存的目录默认是系统的临时目录。

DiskFileItemFactory factory=new DiskFileItemFactory();
        //更改临时文件的存放目录
        factory.setRepository(new File("D:/"));

若是是本身用IO流实现的文件上传,则须要在流关闭后,清理临时文件。

FileItem.delete();

可使用FileItem.write(File f)实现文件上传的保存。

八、中文编码

request.setCharacterEncoding("UTF-8");

    //该编码要和jsp页面保持一致
    String fieldValue=item.getString("UTF-8");

九、动态js控制上传框

<form action="${pageContext.request.contextPath}/servlet/UploadServlet3" method="post" enctype="multipart/form-data">
        name:<input type="text" name="name"/><br/>
        <div id="d1">
            <div>
            photo:<input type="file" name="photo"/><input type="button" value="继续上传" onclick="addFile()"/>
            </div>
        </div>
        <input type="submit" value="上传"/>
    </form>
    <script type="text/javascript"> function addFile(){ var d1 = document.getElementById("d1"); var oldInnerHtml = d1.innerHTML; d1.innerHTML=oldInnerHtml+"<div>photo:<input type='file' name='photo'/><input type='button' value='删除' onclick='deleteOne(this)'/></div>"; } function deleteOne(delBtn){ delBtn.parentNode.parentNode.removeChild(delBtn.parentNode); } </script>

这里写图片描述

4、文件的下载

首先咱们来看一下页面的处理:
新建一个list.jsp文件:

所有资源以下:<br />

   <c:forEach items="${map}" var="me">
        <c:url value="/servlet/DownLoadServlet" var="url">
            <c:param name="filename" value="${me.key}"></c:param>
        </c:url>
        ${me.value}&nbsp;&nbsp;<a href="${url}">下载</a><br/>
    </c:forEach>

要记得引入jstl的核心包 。

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

接下作界面显示的servlet。
咱们以前上传的文件是用GUID作过相应处理的,是一个拼接好的文件名,用来防止文件同名的状况发生,这里用户浏览到的文件固然要和上传的时候的文件名 相同,因此这里咱们要进行截取,把GUID拼接的前缀去掉,以“_”分开。一个是在服务器中防同名的文件名以及显示出来的截取后的文件名。这里之前面说过的,防同名文件方法2:用文件名的hashCode计算须要进行存储的目录,二级目录。这里也就须要使用hashCode找到作过文件的路径。

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //key:GUID文件名 value:old文件名
        Map<String, String> map = new HashMap<String, String>();
        //获取/WEB-INF/files的真实路径
        String rootDirectoryRealPath = getServletContext().getRealPath("/WEB-INF/files");
        //递归遍历找出全部的文件
        System.out.println(rootDirectoryRealPath);
        File rootDirectory = new File(rootDirectoryRealPath);
        treeWalk(rootDirectory,map);
        //存到请求范围中,转发给jsp显示
        request.setAttribute("map", map);
        request.getRequestDispatcher("/list.jsp").forward(request, response);
    }
    //递归遍历找出全部的文件,把文件名高出来
    public void treeWalk(File file, Map<String, String> map) {
        if(file.isFile()){
            String guidFileName = file.getName();
            String oldFileName = guidFileName.substring(guidFileName.indexOf("_")+1);
            map.put(guidFileName, oldFileName);
        }else{
            //目录
            File[] childFiles = file.listFiles(); for(File f:childFiles){ treeWalk(f, map); } } }

接下来就是下载的处理了。

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String guidFilename = request.getParameter("filename");//get方式提交的
        guidFilename = new String(guidFilename.getBytes("ISO-8859-1"),"UTF-8");
        //计算存放路径
        File storeDirectory = new File(getServletContext().getRealPath("/WEB-INF/files"));
        String childDirectory = makeChildDirecotry(storeDirectory, guidFilename);// 13/1
        //构建输入流
        InputStream in = new FileInputStream(new File(storeDirectory,childDirectory+File.separator+guidFilename));

        //用响应对象的输出流输出:下载的方式
        String oldFileName = guidFilename.substring(guidFilename.indexOf("_")+1);
        response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(oldFileName,"UTF-8"));//不适用火狐
        response.setContentType("application/octet-stream");
        OutputStream out = response.getOutputStream();

        int len = -1;
        byte buf[] = new byte[1024];
        while((len=in.read(buf))!=-1){
            out.write(buf, 0, len);
        }
        in.close();


    }
    private String makeChildDirecotry(File storeDirectory, String guidFilename) {

        int hashCode = guidFilename.hashCode();
        int dir1 = hashCode&0xf;
        int dir2 = (hashCode&0xf0)>>4;

        String s = dir1+File.separator+dir2;

        File f = new File(storeDirectory,s);
        if(!f.exists()){
            f.mkdirs();
        }
        return s;
    }

经过这个网址进行访问

http://localhost:8080/UploadDemo/servlet/ShowAllFilesServlet

这里写图片描述

源码下载:https://github.com/sdksdk0/UploadDemo

相关文章
相关标签/搜索