javaWeb学习JavaJavaweb

javaweb 文件上传(fileupload) 下载

2016-10-20  本文已影响144人  lvlvforever

1 文件上传

html中通过<input type="file"/>可以向服务器上传文件。不过后台需要手动解析请求,比较复杂,所以可以使用smartupload或apache的fileupload组件进行文件的上传。smartupload据网友测试,在上传大文件时不稳定,所以还是使用fileupload的吧,毕竟apache出品。

本例子中使用的jar包:

  1. commons-fileupload-1.3.2.jar
  2. commons-io-2.5.jar(fileupload依赖)

前端jsp页面:

    <div class="upload">
        <form action="UploadFileServlet" method="POST" enctype="multipart/form-data">
            上传文件:<input type="file" name="uploadFile">
        <input type="submit" value="upload">上传
        <input type="reset" value="reset">重置
        </form>
        
    </div>

其中有几个需要注意的点:

  1. form表单的enctype必须为"multipart/form-data"。
  2. <input type="file" name="uploadFile"/> 中必须有name属性,因为在fileupload中会根据fieldName解析上传的文件。
  3. method必须为POST方法。
  4. 如果多文件上传的话,file类型的name必须为不同的名称。

服务端需要建立一个UploadFileServlet来处理请求。
关键的doPost方法:

protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub

        // 首先检测是否是文件上传,主要依据enctype的值来判定
        if (!ServletFileUpload.isMultipartContent(request)) {
            PrintWriter writer = response.getWriter();
            writer.write("Error 不是文件上传,表单必须包含 enctype='multipart/form-data'");
            writer.flush();
            writer.close();
            return;
        }

        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置在内存中的缓存大小,如果超过了则保存到临时文件。
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        System.err.println(System.getProperty("java.io.tmpdir"));
        // 设置临时文件夹的目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置单个文件大小的最大值
        upload.setFileSizeMax(MAX_FILE_SIZE);
        // 设置上传文件总量的最大值,包括所有文件和表单的总和
        upload.setSizeMax(MAX_REQUEST_SIZE);
        File uploadDir = new File(UPLOAD_PATH);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        try {
            // 解析request
            List<FileItem> formItems = upload.parseRequest(request);
            if (formItems != null && formItems.size() > 0) {
                for (FileItem item : formItems) {
                    // 如果不是普通的formField则就是上传的文件
                    if (!item.isFormField()) {
                        String fileName = item.getName();
                        File storeFile = new File(UPLOAD_PATH + File.separator
                                + fileName);
                        System.err.println(storeFile.getAbsolutePath());
                        item.write(storeFile);
                        item.delete();

                    } else {
                        System.err.println(item.getString());
                    }
                }
            }
            request.setAttribute("message", "成功");
            request.getRequestDispatcher("result.jsp").forward(request,
                    response);

        } catch (Exception e) {
            // TODO: handle exception
            request.setAttribute("message", e.getMessage());
            request.getRequestDispatcher("result.jsp").forward(request,
                    response);
            e.printStackTrace();
            return;
        }
    }

这样即可实现文件的上传。

需要改进的地方
  1. 没有上传进度
  2. 限制文件大小
  3. 限制文件类型

对于第2点,可以利用fileupload的抛出异常解决。

catch(FileUploadBase.FileSizeLimitExceededException e){
            request.setAttribute("message", "单个文件大小超过限制");
            request.getRequestDispatcher("result.jsp").forward(request,
                    response);
            e.printStackTrace();
            return;
        
        } catch(FileUploadBase.SizeLimitExceededException e){
            request.setAttribute("message", "上传文件总大小超过限制");
            request.getRequestDispatcher("result.jsp").forward(request,
                    response);
            e.printStackTrace();
            return;
        
        }

对于第3点,可以在页面用js进行校验。


    <form action="UploadFileServlet" method="POST"
        enctype="multipart/form-data" onsubmit="return check_file()">
        上传文件:<input type="file" name="uploadFile" id="uploadFile"> <input
            type="submit" value="upload">上传 <input type="reset"
            value="reset">重置

    </form>
    <script>
        function check_file() {
            var fileName = document.getElementById("uploadFile").value;
            var suffix = fileName.substr(fileName.lastIndexOf(".") + 1);
            if (suffix !== "exe") {
                alert("只能上传exe文件");
                return false;
            }

        }
    </script>

对于第1点,fileupload可以添加监听器,监听上传进度。

//设置上传进度的监听器
        ProgressListener progressListener = new ProgressListener() {
            public void update(long pBytesRead, long pContentLength, int pItems) {
                System.out.println("We are currently reading item " + pItems);

                if (pContentLength == -1) {
                    System.out.println("So far, " + pBytesRead
                            + " bytes have been read.");

                } else {
                    System.out.println("So far, " + pBytesRead + " of "
                            + pContentLength + " bytes have been read.");
                    uploadPercent = (double) pBytesRead / pContentLength;
                    System.err.println(uploadPercent);

                }

            }
        };
        upload.setProgressListener(progressListener);

虽然服务端添加了监听器,可以在console或者Log里打印上传进度,但我们想要的是让用户看到上传进度。所以需要把进度返回给用户。
要解决的问题主要有两个:

  1. 进度信息如何保存
  2. 前台如何获取
    其中的一种方案是利用session。我们将上传进度保存在session里,前台通过ajax方法定时获取上传的进度,因为每个用户是一个session,不同的用户session不同。这样当不同的用户同时上传文件时,依然可以正确的获得上传进
    度,不会获取到其他用户上传文件的进度。
    具体首先有一个简单的保存上传进度的实体类:
public class UploadStatus {
    private double percent;

    public double getPercent() {
        return percent;
    }

    public void setPercent(double percent) {
        this.percent = percent;
    }
}

然后有一个监听的类,实现了ProgressListener接口

public class UploadListener implements ProgressListener{
    private UploadStatus status;
    public  UploadListener(UploadStatus status) {
        
        // TODO Auto-generated constructor stub
        this.status = status;
    }
    @Override
    public void update(long pBytesRead, long pContentLength, int pItems) {
        
        // TODO Auto-generated method stub
        double uploadPercent = (double) pBytesRead / pContentLength;
        status.setPercent(uploadPercent);
    }

}

在doPost方法中:

//设置上传进度的监听器
        UploadStatus status = new UploadStatus();
        UploadListener listener = new UploadListener(status);
        upload.setProgressListener(listener);
        request.getSession(true).setAttribute("uploadStatus", status);

最后在doGet方法中,返回上传进度。

protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "no-store");
        response.setDateHeader("Expires", 0);
        response.setHeader("Pragrma", "no-cache");
        PrintWriter writer = response.getWriter();
        UploadStatus status = (UploadStatus)(request.getSession().getAttribute("uploadStatus"));
        if(status != null){
            writer.write("上传进度:" + status.getPercent());
        }else{
            writer.write("没有上传信息");
        }
        writer.flush();
        writer.close();
    }

当用户上传时可以访问doGet方法即可获取进度。
前台页面可以这样写:
其中form的target属性可以防止页面跳转。

<body>
<iframe width=0 height=0 name="uploadFrame"></iframe>
    <form action="UploadFileServlet" method="POST"
        enctype="multipart/form-data" target="uploadFrame" onsubmit="getStatus()">
        上传文件:<input type="file" name="uploadFile" id="uploadFile"> <input
            type="submit" value="upload">上传 <input type="reset"
            value="reset">重置

    </form>
    <span>上传进度:</span><span id="progress"></span>
    <script src="jquery-1.11.2.js"></script>
    <script>
    var finished ;
        function check_file() {
            var fileName = document.getElementById("uploadFile").value;
            var suffix = fileName.substr(fileName.lastIndexOf(".") + 1);
            if (suffix !== "exe") {
                //alert("只能上传exe文件");
                //return false;
            }
            finished = false;
            return true;

        }
        function getStatus(){
            finished = false;
            console.log("finished = " + finished)

            showStatus();
        }
        
        function showStatus(){
            console.log("showstatus finished = " + finished)

            if(finished === true) return;
            $.ajax({
                url:'UploadFileServlet',
                type:'GET',
                success:function(data){
                    $('#progress').text(data);
                    if(data == '1.0'){
                        finished = true;
                    }
                        
                },
                error:function(data){
                    alert(data);
                }
            
            });
            setTimeout(showStatus,1000);
        }
        
    </script>
</body>

最后 完整的程序UploadFile.zip

2 文件下载

文件下载相对比较简单

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        String id = request.getParameter("id");
        //app.properties 路径
        DFileItem dao = new DFileItem();
        String basePath = request.getSession().getServletContext().getRealPath("");
        String configPath = basePath+Constants.CONFIG_PATH;
        String uploadPath = Configuration.getInstance(configPath).getProperty(Constants.FILE_STORAGE_PATH);
        FileItem item = dao.queryFileItem(id);
        if(item == null){
            PrintWriter writer = response.getWriter();

            writer.write("文件已经被删除");
            writer.close();
            return;
        }
        
        String name = item.getFileName();
        response.setContentType("application/octet-stream");
        String downloadName = URLEncoder.encode(name,"UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'"+downloadName);
        File file = new File(uploadPath+item.getUploadTime());
        response.setContentLength((int)file.length());
        FileInputStream in = new FileInputStream(file);
        OutputStream out = response.getOutputStream();
        byte[] buffer = new byte[8192];
        int len = 0;
        while((len = in.read(buffer)) != -1){
            out.write(buffer, 0, len);
        }
        out.flush();
        out.close();
        in.close();
    }

注意response 的contentType和header的设置即可。
最终一个完整的文件上传下载的例子FileServer

上一篇下一篇

猜你喜欢

热点阅读