zoukankan      html  css  js  c++  java
  • Java结合WebUploader文件上传

            之前自己写小项目的时候也碰到过文件上传的问题,没有找到很好的解决方案。虽然之前网找各种解决方案的时候也看到过WebUploader,但没有进一步深究。这次稍微深入了解了些,这里也做个小结。

    简单的文件和普通数据上传并保存

     JSP页面:

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
            文件:<input type="file" value="请选择文件" name="file" /> <br/>
            信息:<input type="text" name="info" /> <br/>
            <input type="submit" value="提交" />
        </form>
    </body>
    </html>

     servlet:

    package com.yihengliu.web.action;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    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.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    import org.apache.commons.io.FileUtils;
    
    /**
     * Servlet user to accept file upload
     */
    public class FileUploadServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
    
        private String serverPath = "e:/";
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            response.getWriter().append("Served at: ").append(request.getContextPath());
    
            System.out.println("进入后台...");
    
            // 1.创建DiskFileItemFactory对象,配置缓存用
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
    
            // 2. 创建 ServletFileUpload对象
            ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
    
            // 3. 设置文件名称编码
            servletFileUpload.setHeaderEncoding("utf-8");
    
            // 4. 开始解析文件
            try {
                List<FileItem> items = servletFileUpload.parseRequest(request);
                for (FileItem fileItem : items) {
    
                    if (fileItem.isFormField()) { // >> 普通数据
                        String info = fileItem.getString("utf-8");
                        System.out.println("info:" + info);
                    } else { // >> 文件
                        // 1. 获取文件名称
                        String name = fileItem.getName();
                        // 2. 获取文件的实际内容
                        InputStream is = fileItem.getInputStream();
    
                        // 3. 保存文件
                        FileUtils.copyInputStreamToFile(is, new File(serverPath + "/" + name));
                    }
    
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
    }

     使用WebUploader组件上传

          分片、并发,预览、压缩,多途径添加文件夹(文件多选,拖拽等),妙传

    • 页面样式使用

      

    <html>
    <title>使用webuploader上传</title>
    <!-- 1.引入文件 -->
    <link rel="stylesheet" type="text/css" href="${pageContext.request.contextPath }/js/webuploader.css">
    <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-2.1.4.min.js"></script>
    <script type="text/javascript" src="${pageContext.request.contextPath }/js/webuploader.js"></script>
    </head>
    <body>
        <!-- 2.创建页面元素 -->
        <div id="upload">
            <div id="filePicker">文件上传</div>
        </div>
        <!-- 3.添加js代码 -->
        <script type="text/javascript">
            var uploader = WebUploader.create(
                {
                    swf:"${pageContext.request.contextPath }/js/Uploader.swf",
                    server:"${pageContext.request.contextPath }/FileUploadServlet",
                    pick:"#filePicker",
                    auto:true
                }       
            );
        </script>
    </body>
    </html>
    • 生成文件名列表、实时显示上传进度、显示缩略图
    • 增加文件列表div, <div id="fileList"></div>

    生成缩略图和显示上传进度

    // 生成缩略图和上传进度
    uploader.on("fileQueued", function(file) {
            // 把文件信息追加到fileList的div中
            $("#fileList").append("<div id='" + file.id + "'><img/><span>" + file.name + "</span><div><span class='percentage'><span></div></div>")
    
            // 制作缩略图
            // error:不是图片,则有error
            // src:代表生成缩略图的地址
            uploader.makeThumb(file, function(error, src) {
                if (error) {
                    $("#" + file.id).find("img").replaceWith("<span>无法预览&nbsp;</span>");
                } else {
                    $("#" + file.id).find("img").attr("src", src);
                }
            });
        }
    );
    
    // 监控上传进度
    // percentage:代表上传文件的百分比
    uploader.on("uploadProgress", function(file, percentage) {
        $("#" + file.id).find("span.percentage").text(Math.round(percentage * 100) + "%");
    });
    • 拖拽上传、粘贴上传

                      创建拖拽区域并设置样式:

    <style type="text/css">
        #dndArea {
            width: 200px;
            height: 100px;
            border-color: red;
            border-style: dashed;
        }
    </style>        
    
    <!-- 创建用于拖拽的区域 -->
    <div id="dndArea"></div>
    • 基本配置中增加dnd区域配置(开启拖拽)

      屏蔽拖拽区域外的响应

      开启粘贴功能
      

    var uploader = WebUploader.create(
        {
            swf:"${pageContext.request.contextPath }/js/Uploader.swf",
    server:"${pageContext.request.contextPath }/FileUploadServlet",
            pick:"#filePicker",
            auto:true,
            // 开启拖拽
            dnd:"#dndArea",
            // 屏蔽拖拽区域外的响应
            disableGlobalDnd:true,
            // 
        }       
    );
    • 文件的分块上传

    前端根据需要发送的文件生成一个md5字符串发送给后台,后台创建以该md5字符串命名的文件夹。前端分块发送文件并发送文件块序号给后台,后台接收到文件后按序号名称保存。前端发送完成后通知后台合并文件。

    前端配置,开启是否分块、分块大小、线程个数等

    // 上传基本配置
    var uploader = WebUploader.create(
    {
        swf:"${pageContext.request.contextPath }/js/Uploader.swf",
        server:"${pageContext.request.contextPath }/FileUploadServlet",
        pick:"#filePicker",
        auto:true,
        dnd:"#dndArea",
        disableGlobalDnd:true,
        paste:"#uploader",
    
        // 分块上传设置
        // 是否分块
        chunked:true,
        // 每块文件大小(默认5M)
        chunkSize:5*1024*1024,
        // 开启几个并非线程(默认3个)
        threads:3,
        // 在上传当前文件时,准备好下一个文件
        prepareNextFile:true
    }       
    );
    • 前端监听分块

    可以分为三个时间点:

      • before-send-file: 该方法在文件上传前调用(只会在一个文件上传前调用)。
        可以在该方法中获取文件的md5字符串作为后台保存分块文件的目录名
      • before-send: 该方法在每个分块文件上传前调用(每个分块上传前都会调用)。
        可以在该方法中发送md5字符串到后台,后台判断是否已经存在分块决定是否发送以达到断点续传的功能
      • after-send-file: 该方法在所有文件上传完成没有错误之后调用(所有分块上传完成后调用)。
        可以在该方法中通知后台合并所有分块
    • 前端获取文件md5字符串,发送每个分块时发送到后台,后台接收如果不存在文件夹创建文件夹,保存分块发送的文件
        // 监听分块上传的时间点,断点续传
    var fileMd5;
    WebUploader.Uploader.register({
        "before-send-file":"beforeSendFile",
        "before-send":"beforeSend",
        "after-send-file":"afterSendFile"
        },{
            beforeSendFile:function(file) {
                // 创建一个deffered,用于通知是否完成操作
                var deferred = WebUploader.Deferred();
    
                // 计算文件的唯一标识,用于断点续传和妙传
                (new WebUploader.Uploader()).md5File(file, 0, 5*1024*1024)
                    .progress(function(percentage){
                        $("#"+file.id).find("span.state").text("正在获取文件信息...");
                    })
                    .then(function(val) {
                        fileMd5 = val;
                        $("#" + file.id).find("span.state").text("成功获取文件信息");
                        // 放行
                        deferred.resolve();
                    });
                // 通知完成操作
                return deferred.promise();
            },
            beforeSend:function() {
                var deferred = WebUploader.Deferred();
                // 发送文件md5字符串到后台
                this.owner.options.formData.fileMd5 = fileMd5;
                deferred.resolve();
                return deferred.promise();
            },
            afterSendFile:function() {
    
            }
        }
    );

    添加state标签

    $("#fileList").append("<div id='" + file.id + "'><img/><span>" + file.name + "</span><div><span class='state'></span></div><div><span class='percentage'></span></div></div>");

    保存文件

    // 4. 开始解析文件
    // 文件md5获取的字符串
    String fileMd5 = null;
    // 文件的索引
    String chunk = null;
    try {
        List<FileItem> items = servletFileUpload.parseRequest(request);
        for (FileItem fileItem : items) {
    
            if (fileItem.isFormField()) { // >> 普通数据
                String fieldName = fileItem.getFieldName();
                if ("info".equals(fieldName)) {
                    String info = fileItem.getString("utf-8");
                    System.out.println("info:" + info);
                }
                if ("fileMd5".equals(fieldName)) {
                    fileMd5 = fileItem.getString("utf-8");
                    System.out.println("fileMd5:" + fileMd5);
                }
                if ("chunk".equals(fieldName)) {
                    chunk = fileItem.getString("utf-8");
                    System.out.println("chunk:" + chunk);
                }
            } else { // >> 文件
                /*// 1. 获取文件名称
                String name = fileItem.getName();
                // 2. 获取文件的实际内容
                InputStream is = fileItem.getInputStream();
    
                // 3. 保存文件
                FileUtils.copyInputStreamToFile(is, new File(serverPath + "/" + name));*/
    
                // 如果文件夹没有创建文件夹
                File file = new File(serverPath + "/" + fileMd5);
                if (!file.exists()) {
                    file.mkdirs();
                }
                // 保存文件
                File chunkFile = new File(serverPath + "/" + fileMd5 + "/" + chunk);
                FileUtils.copyInputStreamToFile(fileItem.getInputStream(), chunkFile);
    
            }
    
        }
    • 前端通知action进行合并文件

        前端增加:

    // 通知合并分块
    $.ajax(
        {
            type:"POST",
            url:"${pageContext.request.contextPath}/UploadActionServlet?action=mergeChunks",
            data:{
                fileMd5:fileMd5
            },
            success:function(response){
    
            }
        }
    );

    新增合并action:

    package com.yihengliu.web.action;
    
    import java.io.File;
    import java.io.FileFilter;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.channels.FileChannel;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.UUID;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 合并上传文件
     */
    public class UploadActionServlet extends HttpServlet {
        private static final long serialVersionUID = 1L;
    
        private String serverPath = "e:/";
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            System.out.println("进入合并后台...");
            String action = request.getParameter("action");
            if ("mergeChunks".equals(action)) {
                // 获得需要合并的目录
                String fileMd5 = request.getParameter("fileMd5");
    
                // 读取目录所有文件
                File f = new File(serverPath + "/" + fileMd5);
                File[] fileArray = f.listFiles(new FileFilter() {
    
                    // 排除目录,只要文件
                    @Override
                    public boolean accept(File pathname) {
                        if (pathname.isDirectory()) {
                            return false;
                        }
                        return true;
                    }
                });
    
                // 转成集合,便于排序
                List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));
                // 从小到大排序
                Collections.sort(fileList, new Comparator<File>() {
    
                    @Override
                    public int compare(File o1, File o2) {
                        if (Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())) {
                            return -1;
                        }
                        return 1;
                    }
    
                });
    
                // 新建保存文件
                File outputFile = new File(serverPath + "/" + UUID.randomUUID().toString() + ".zip");
    
                // 创建文件
                outputFile.createNewFile();
    
                // 输出流
                FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
                FileChannel outChannel = fileOutputStream.getChannel();
    
                // 合并
                FileChannel inChannel;
                for (File file : fileList) {
                    inChannel = new FileInputStream(file).getChannel();
                    inChannel.transferTo(0, inChannel.size(), outChannel);
                    inChannel.close();
    
                    // 删除分片
                    file.delete();
                }
    
                // 关闭流
                fileOutputStream.close();
                outChannel.close();
    
                // 清除文件加
                File tempFile = new File(serverPath + "/" + fileMd5);
                if (tempFile.isDirectory() && tempFile.exists()) {
                    tempFile.delete();
                }
    
                System.out.println("合并文件成功");
    
            }
        }
    
        protected void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doGet(request, response);
        }
    
    }
    • 断点续传
      • 前端页面发送前添加校验,校验是否已经上传分块
        beforeSend:function(block) {
                        var deferred = WebUploader.Deferred();
        
                        // 支持断点续传,发送到后台判断是否已经上传过
                        $.ajax(
                            {
                                type:"POST",
                                url:"${pageContext.request.contextPath}/UploadActionServlet?action=checkChunk",
                                data:{
                                    // 文件唯一表示                               
                                    fileMd5:fileMd5,
                                    // 当前分块下标
                                    chunk:block.chunk,
                                    // 当前分块大小
                                    chunkSize:block.end-block.start
                                },
                                dataType:"json",
                                success:function(response) {
                                    if(response.ifExist) {
                                        // 分块存在,跳过该分块
                                        deferred.reject();
                                    } else {
                                        // 分块不存在或不完整,重新发送
                                        deferred.resolve();
                                    }
                                }
                            }
                        );
        
        
                        // 发送文件md5字符串到后台
                        this.owner.options.formData.fileMd5 = fileMd5;
                        return deferred.promise();
                    }
    • action中添加校验
     else if ("checkChunk".equals(action)) {
            // 校验文件是否已经上传并返回结果给前端
    
            // 文件唯一表示                               
            String fileMd5 = request.getParameter("fileMd5");
            // 当前分块下标
            String chunk = request.getParameter("chunk");
            // 当前分块大小
            String chunkSize = request.getParameter("chunkSize");
    
            // 找到分块文件
            File checkFile = new File(serverPath + "/" + fileMd5 + "/" + chunk);
    
            // 检查文件是否存在,且大小一致
            response.setContentType("text/html;charset=utf-8");
            if (checkFile.exists() && checkFile.length() == Integer.parseInt((chunkSize))) {
                response.getWriter().write("{"ifExist":1}");
            } else {
                response.getWriter().write("{"ifExist":0}");
            }
        }
  • 相关阅读:
    Microsoft.mshtml到底是什么?
    编译.NET程序自动产生版本号的规则
    VB.NET(2005)中关于dll调用的错误信息(转)
    C# RichTextBox控件 滚动条 自动移至 最后一条记录处
    如何刪除richtextbox最後一行
    Using Hudson for C++/CMake/CppUnit
    让开发自动化: 选择持续集成服务器
    C#加载窗体错误
    各种CI工具对比表
    用 Hudson 实现 Visual Studio (C++) 项目的 daily build
  • 原文地址:https://www.cnblogs.com/dongyu666/p/7636911.html
Copyright © 2011-2022 走看看