zoukankan      html  css  js  c++  java
  • java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载

    java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载

    实现功能:zip文件上传,后台自动解压,Jstree树目录(遍历文件),editor.md预览

    采用Spring+SpringMVC+Maven+Jstree+editor.md实现,主要功能:

    • zip压缩文件的上传
    • 后台自动解压
    • Jstree自动获取最上层目录,每次仅仅会获取当前层的文件或者文件夹,然后点击文件夹或者文件,通过ajax与服务器交换数据,减轻检索和数据传输压力
    • 后台通过文件路径遍历文件夹
    • 通过editor.md将文本代码高亮显示
    • 图片的解析预览

    总体项目目录结构:

    预览:

    点击提交后:

    并提供下载功能

    1. 分析代码

    上传压缩包的html代码,使用velocity模板渲染引擎:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>上传压缩项目包</title>
    </head>
    <body>
    提示:压缩包内请勿包含中文!
    <div class="uploadZipFile" id="uploadZipFile">
        <form name="zipForm" id="zipForm">
            <input type="text" id="file-name" name="file-name" placeholder="请输入项目名称"/>
            <div class="file-name-check" style="color: red"></div>
            <br>
            <input type="file"  name="file-zip" id="file-zip"/>
            <br>
            <input type="button" class="" id="upload-zip" value="提交"/>
        </form>
    </div>
    
    </body>
    <script src="//cdn.bootcss.com/jquery/1.11.1/jquery.min.js"></script>
    <script type="text/javascript">
        $(window).load(function() {
            //当鼠标移出输入框
            $('#file-name').on('blur', function(){
                var fileName = document.getElementById("file-name").value;
                if(fileName==''){
                    $('.file-name-check').html('');
                    $('.file-name-check').append("请输入项目名!")
                }
            });
    
            $("#file-zip").bind("change",function(){
                var imgArr = ["zip"];
                if($(this).val() == "")
                {
                    alert("请选择文件!");
                }
                else{
                    var file = $(this).val();
                    var len = file.length;
                    var ext = file.substring(len-3,len).toLowerCase();
                    if($.inArray(ext,imgArr) == -1)
                        alert("不是zip格式");
                }
            });
    
            $('#upload-zip').on('click', function(){
                var form = document.getElementById("zipForm");
                if(document.getElementById("file-name").value==''){		//当项目名为空时
                    alert("请输入项目名!");
                    return false;
                }
                if(document.getElementById("file-zip").value==''){		//当项目为空时
                    alert("请上传项目!");
                    return false;
                }
    
                var formdata = new FormData(form);
                $.ajax({
                    url:"/admin/file/zip/upload",
                    data: formdata,
                    type:"post",
                    //预期服务器返回的数据类型,自动解析json返回值,不设置的话可能要执行oResult = JSON.parse(oResult);进行解析
                    dataType:"json",
                    //默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),
                    // 都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
                    processData: false,
                    //contentType: false,避免 JQuery 对data操作,可能失去分界符,而使服务器不能正常解析文件。
                    contentType: false,
                    success: function(oResult) {
    //                    console.log(oResult);
                        if(oResult.success==1){
                            window.location.href="/admin/file/zip/show?file-path="+oResult.url;
                        }else{
                            alert(oResult.message);
                        }
                    }
                })
    //              .done(function(oResult) {   //注意done表示成功,fail表示失败,always表示不论成功还是失败,会执行的函数,
    //                                          //但是在低版本jquery不兼容,是高版本jquery推荐
    //                if(oResult.success==1){
    //                    window.location.href="/";
    //                    alert(oResult.message);
    //                }else{
    //                    alert(oResult.message);
    //                }
    //            }).fail(function () {
    //                alert('出现错误,请重试');
    //            });
            })
        });
    </script>
    
    </html>
    

    预览自动解压后文件夹的html代码,使用velocity模板渲染引擎:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>项目展示</title>
        <link rel="stylesheet" type="text/css" href="/css/editormd.min.css">
        <link rel="stylesheet" type="text/css" href="/css/style.css">
    </head>
    <!-- 禁用复制粘贴-->
    <body oncontextmenu=self.event.returnValue=false onselectstart="return false">
    <!-- 搜索表单-->
    <form id="s" class="search">
        <input type="search" id="q" />
        <button type="submit">Search</button>
    </form>
    <!-- 下载按钮-->
    <div class="action">
        <input type="hidden" id="filePathRem" value="$!{filePath}">
        <a href="/admin/file/zip/download?file-path=$!{filePath}">下载</a>
    </div>
    <!-- 放JStree目录树-->
    <div id="container" class="side-nav"></div>
    <!-- 放editor.md文本-->
    <div id="markdown-editor" class="markdown-text"></div>
    <!-- 放图片-->
    <div id="image-panel" class="image-panel"></div>
    </body>
    <!--jstree官网https://github.com/vakata/jstree#readme-->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.3/themes/default/style.min.css" />
    <script src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.3/jstree.min.js"></script>
    <script src="/js/file-node.js"></script>
    
    <script src="/js/editormd.min.js"></script>
    ##支持markdown快速解析
    <script src="/lib/marked.min.js"></script>
    ##支持代码高亮
    <script src="/lib/prettify.min.js"></script>
    </html>
    

    对应的JS文件,file-node.js:

    $(function() {
        var filePath = document.getElementById("filePathRem").value;
        //注意这里面只能处理寻找文件夹的子文件或者子文件夹事件,可以把文件的读取写到 $('#container').on("changed.jstree", function (e, data)函数中
        $('#container').jstree({
            'core': {
                'data':
                //node为点击的节点,cd为输出结果的函数
                    function (node, cb) {
                        var formdata = new FormData();
                        formdata.append("file-path",filePath);
                        formdata.append("id",node.id);
                        //通过表单对象上传文件或者数据,设置
                        // processData: false,表示不要对data参数进行序列化处理
                        //contentType: false,避免 JQuery 对data操作,可能失去分界符,而使服务器不能正常解析文件。
                        $.ajax({
                            //不要用get方法,因为#在浏览器中有特殊含义, 
                            // #代表网页中的一个位置。其右面的字符,就是该位置的标识符。比如,http://www.example.com/index.html#print就代表网页index.html的print位置。
                            // 浏览器读取这个URL后,会自动将print位置滚动至可视区域。
                            //并且在发送的请求中,自动忽略#,而首次打开页面的第一次请求id=#
                            //url: "/admin/file/zip/show.action?lazy&file-path=" + filePath + "&id=" + node.id,
                            url:"/admin/file/zip/show.action",
                            data:formdata,
                            type:"post",
                            dataType:"json",
                            processData: false,
                            contentType: false,
                            success: function (oResult) {
                                if (oResult.result.success == 1) {
                                    cb(oResult.array);
                                } else {
                                    alert(oResult.result.message);
                                }
                            }
                        })
                    }
            },
            //0为文件夹,即默认,1为文件
            "types" : {
                0 : {
                    "icon" : "glyphicon glyphicon-folder",
                    "valid_children" : []
                },
                1 : {
                    "icon" : "glyphicon glyphicon-file"
                }
            },
            //搜索功能插件和类别插件,以对文件夹和文有不同的图标
            "plugins" : ["search","types"]
        });
    
        //上面的表单s和本函数都用于搜索,模糊搜索,不区分大小写
        $("#s").submit(function(e) {
            e.preventDefault();
            $("#container").jstree(true).search($("#q").val());
        });
    
        //注意changed与click的区别,前者只要状态不变,点击多少次都加载一次,后者每次点击都重新加载
        $('#container').on("changed.jstree", function (e, data) {
            // console.log("The selected nodes are:");
            // //显示被选择节点id编号
            // console.log(data.selected);
            // //显示被选择节点的命名
            // console.log(data.node.text);
            var name=String(data.selected);
            //如果包含.则为请求文件
            if(name.search("\.")>1){
                //判断是否是图片,其他文件都是读取Json字符串的形式
                if(!isImage(name)){
                    var formdata = new FormData();
                    formdata.append("file-path",filePath);
                    formdata.append("id",name);
                    $.ajax({
                        url:"/admin/file/zip/show.action",
                        data:formdata,
                        type:"post",
                        dataType:"json",
                        processData: false,
                        contentType: false,
                        success: function (oResult) {
                            if (oResult.result.success == 1) {
                                //首先把页面中的可能存在的图片清空
                                document.getElementById("image-panel").innerHTML ='';
                                //由于editor.md每次更新内容之后都会将<textarea id="append-test" style="display:none;"></textarea>删除,那么每次更新前都需要添加
                                document.getElementById("markdown-editor").innerHTML='<textarea id="append-test" style="display:none;"></textarea>';
                                document.getElementById("append-test").value="```
    "+oResult.fileContent+"
    ```";
                                //用于将markdown文本转化为html格式
                                editormd.markdownToHTML("markdown-editor", {
                                });
                            } else {
                                alert(oResult.result.message);
                            }
                        }
                    })
                }else {    //对于图片,我们要显示为图片,而不是文本的字符流
                    document.getElementById("markdown-editor").innerHTML='';
                    document.getElementById("image-panel").innerHTML = '<img width="500" id="img-circle" src="">';
                    document.getElementById("img-circle").src = "/admin/file/zip/image.action?file-path="+filePath+"&id="+name;
                }
            }
        });
        //判断请求文件是否是图片,仅支持常用类型
        function isImage(objFile) {
            var objtype = objFile.substring(objFile.lastIndexOf(".")).toLowerCase();
            var fileType = new Array(".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico");
            for (var i = 0; i < fileType.length; i++) {
                if (objtype == fileType[i]) {
                    return true;
                    break;
                }
            }
            return false;
        }
    });
    

    对应Controller层代码,FileController.java:

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.alibaba.fastjson.JSONObject;
    import com.demo.fileTree.model.FileHandleResponse;
    import com.demo.fileTree.model.JstreeNode;
    import com.demo.fileTree.service.FileService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.util.List;
    
    /**
     * 实现项目zip压缩包的上传,自动解压,解压后的预览,包括文本和字符串,项目的压缩下载,
     * 由于java.util.zip包不支持汉字的问题,在项目压缩包内请勿包含中文文件名,但是在页面中的项目名可以起名为中文,
     * 可以用org.apache.tools.zip压缩/解压缩zip文件,解决中文乱码问题。
     *
     * @author xie
     * @version 1.0
     * @Date 2017/5/26
     */
    @Controller
    public class FileController {
        @Autowired
        FileService fileService;
    
        /**
         * 主页
         * @return
         */
        @RequestMapping(path = {"/"}, method = {RequestMethod.GET, RequestMethod.POST})
        public String index() {
            return "upload_zip";
        }
    
        /**
         * 上传压缩zip项目文件
         * @param file zip压缩文件
         * @param fileName 项目的命名,我们将解压缩的文件放到以项目名命名的文件夹内,为了保证项目名重复的也可以上传,项目名文件夹外部还有一个32位UUID命名的文件夹,
         *                 只不过取出项目时没有显示
         * @return 结果的json字符串
         */
        @RequestMapping(path = {"/admin/file/zip/upload"}, method = {RequestMethod.GET, RequestMethod.POST})
        @ResponseBody
        public String uploadZipFile(@RequestParam("file-zip") MultipartFile file,@RequestParam("file-name")String fileName) {
            FileHandleResponse fileHandleResponse = new FileHandleResponse();
            try {
                if(file.isEmpty()){
                    fileHandleResponse.setSuccess(0);
                    fileHandleResponse.setMessage("上传压缩文件为空");
                    return JSON.toJSONString(fileHandleResponse);
                }
                fileHandleResponse = fileService.uploadFileZip(file,fileName);
                return JSON.toJSONString(fileHandleResponse);
            }catch (Exception e) {
                fileHandleResponse.setSuccess(0);
                fileHandleResponse.setMessage("服务器异常!");
                fileHandleResponse.setUrl(null);
                return JSON.toJSONString(fileHandleResponse);
            }
        }
    
        /**
         * 展示上传的zip项目解压缩后的文件结构
         * @param filePath 项目的路径,比如,C:homemyblogproject2d76c7aa844b4585a53d982d205099e2123其中123为项目名,
         * @param model
         * @return
         */
        @RequestMapping(path = {"/admin/file/zip/show"}, method = {RequestMethod.GET, RequestMethod.POST})
        public String showZipFile(@RequestParam("file-path")String filePath, Model model) {
            model.addAttribute("filePath",filePath);
            //filePath地址大概样子,C:homemyblogproject2d76c7aa844b4585a53d982d205099e2123\,windows和linux不同,
            // 包含文件名,我们提取出来,作为fileName,分隔符可能为/或或\,其中要转意为\
            String fileName = filePath.split("\|\\|/")[filePath.split("\|\\|/").length-1];
            model.addAttribute("fileName",fileName);
            return "show_zip";
        }
    
        /**
         * 项目展示页面
         * @param filePath 项目路径
         * @param relativePath 节点相比项目路径的相对路径,比如项目路径:
         *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
         *                     那么节点路径src/main/java/表示
         *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/src/main/java/
         * @return 对于文件,返回字符内容的json字符串,对于文件夹,返回文件夹的下一级所有子文件和子文件夹,其实若文件是图片,我们在下面的getImage()方法中处理
         */
        @RequestMapping(path = {"/admin/file/zip/show.action"}, method = {RequestMethod.GET, RequestMethod.POST})
        @ResponseBody
        public String showZipFileDetail(@RequestParam("file-path") String filePath, @RequestParam("id") String relativePath, Model model) {
            FileHandleResponse fileHandleResponse = new FileHandleResponse();
            try {
                if (relativePath.equals("#")) {    //表示第一次打开页面的请求,relativePath为#,没什么意义,设为空字符串
                    relativePath = "";
                }
                File file = new File(filePath+relativePath);
    
                //如果请求路径存在,即文件或者目录存在
                if (file.exists()) {
                    //分为文件或者文件夹两种情况
                    if (file.isFile()) {
                        BufferedReader bufferedReader;
                        try {
                            StringBuilder stringBuilder = new StringBuilder();
                            //将字节流向字符流的转换,并创建字符流缓冲区
                            bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
                            // 每次读入一行
                            String read;
                            //每读入一行,要加一个换行符
                            String lineText="
    ";
                            while ((read = bufferedReader.readLine()) != null) {
                                stringBuilder.append(read+lineText);
                            }
                            bufferedReader.close();
                            fileHandleResponse.setSuccess(1);
                            fileHandleResponse.setMessage("请求成功!");
                            model.addAttribute("result", fileHandleResponse);
                            model.addAttribute("fileContent", stringBuilder.toString());
                            return JSON.toJSONString(model);
                        } catch (Exception e1) {
                            e1.printStackTrace();
                        }
                    } else {
                        List<JstreeNode> list = fileService.getAllChildrenNode(filePath,relativePath);
                        JSONArray jsonArray = new JSONArray();
                        for(JstreeNode jstreeNode : list){
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("id", jstreeNode.getId());
                            jsonObject.put("text", jstreeNode.getText());
                            jsonObject.put("children", jstreeNode.isHasChildren());
                            jsonObject.put("type",jstreeNode.getType());
                            jsonArray.add(jsonObject);
                        }
                        fileHandleResponse.setSuccess(1);
                        fileHandleResponse.setMessage("请求成功!");
                        model.addAttribute("result", fileHandleResponse);
                        //最好不要直接传递list,前端不可以很好的解析
                        model.addAttribute("array", jsonArray);
                        return JSON.toJSONString(model);
                    }
                } else {            //如果请求路径不存在
                    fileHandleResponse.setSuccess(0);
                    fileHandleResponse.setMessage("请求路径不存在!");
                    model.addAttribute("result",fileHandleResponse);
                    return JSON.toJSONString(model);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * 将项目压缩后以字节流的方式发送
         * @param filePath 项目路径
         * @param response
         */
        @RequestMapping(path = {"/admin/file/zip/download"}, method = {RequestMethod.GET})
        public void downloadZipFile(@RequestParam("file-path")String filePath, HttpServletResponse response) {
            FileHandleResponse fileHandleResponse;
            try {
                fileHandleResponse = fileService.downloadFileZip(filePath);
                //地址大概样子,C:homemyblogproject2d76c7aa844b4585a53d982d205099e2123.zip,windows和linux不同,
                // 包含文件名,我们提取出来,作为fileName,分隔符可能为/或或\,其中要转意为\
                String fileName = fileHandleResponse.getUrl().split("\|/|\\")[fileHandleResponse.getUrl().split("\|/|\\").length-1];
                response.setContentType("application/zip");
                response.setCharacterEncoding("UTF-8");
    
                response.setHeader("Content-Disposition","attachment;filename="+fileName);
                OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                byte[] data = fileService.toByteArray(fileHandleResponse.getUrl());
                outputStream.write(data);
                outputStream.flush();
                outputStream.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 按照图片路径查找图片
         * @param filePath 项目路径
         * @param relativePath 节点相比项目路径的相对路径,比如项目路径:
         *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
         *                     那么节点路径src/main/java/表示
     *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/src/main/java/
         * @param response
         */
        @RequestMapping(path = "/admin/file/zip/image.action")
        public void getImage(@RequestParam("file-path") String filePath,
                                      @RequestParam("id") String relativePath,
                                      HttpServletResponse response) {
            try {
                byte[] data = fileService.toByteArray(filePath+relativePath);
                response.setCharacterEncoding("UTF-8");
                OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
                outputStream.write(data);
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    对应Service层代码,FileService.java:

    import com.demo.fileTree.configuration.GlobalConfig;
    import com.demo.fileTree.model.FileHandleResponse;
    import com.demo.fileTree.model.JstreeNode;
    import com.demo.fileTree.utils.ZipUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * 压缩文件上传,并且解压缩后放到服务器响应目录下,
     * 为什么不直接放压缩包,因为别人看一次,需要解压缩一次,也很浪费系统资源
     *
     * @author xie
     * @version 1.0
     * @Date 2017/5/27
     */
    @Service
    public class FileService {
    
        @Autowired
        ZipUtils zipUtils;
    
        /**
         * 默认上传zip压缩格式
         * @param file 上传的文件
         * @return 上传的结果UploadResponse对象
         * @throws IOException
         */
        public FileHandleResponse uploadFileZip(MultipartFile file, String fileName) throws IOException {
          FileHandleResponse fileHandleResponse;
            try {
                fileHandleResponse = zipUtils.unZipFiles(zipUtils.getZipDir(), fileName, file);
                return fileHandleResponse;
            } catch (Exception e) {
                // 请求失败时打印的异常的信息
                fileHandleResponse = new FileHandleResponse();
                fileHandleResponse.setSuccess(0);
                fileHandleResponse.setMessage("服务器异常!");
                return fileHandleResponse;
            }
        }
    
        /**
         *  下载压缩后的项目文件
         *
         * @param filePath 项目路径
         * @return 文件处理结果实体,其中url表示项目压缩后的路径
         * @throws IOException
         */
        public FileHandleResponse downloadFileZip(String filePath) throws IOException {
            FileHandleResponse fileHandleResponse;
            try {
                fileHandleResponse = zipUtils.zipFiles(filePath);
                return fileHandleResponse;
            } catch (Exception e) {
                // 请求失败时打印的异常的信息
                fileHandleResponse = new FileHandleResponse();
                fileHandleResponse.setSuccess(0);
                fileHandleResponse.setMessage("服务器异常!");
                return fileHandleResponse;
            }
        }
    
        /**
         *  返回某一结点(即文件夹)的下一级所有子节点,注意这里输入的不是具体文件或者不存在的路径,是已经判定存在的文件夹路径,
         *  如果是请求具体文件或者不存在的路径,在上一层controller层就应该将文件内容读取并返回或者返回错误信息
         *
         * @param filePath 项目路径
         * @param relativePath 节点相比项目路径的相对路径,比如项目路径:
         *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
         *                     那么节点路径src/main/java/表示
         *                     C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/src/main/java/
         *                     但是由于files[i].getName()只会获得abc这样的单层目录名或者abc.java这样的文件名,因此我们要设置下一级的相对路径为;
         *                     relativePath+files[i].getName()(如果是路径,还要包含/)
         *
         * @return 所有子节点的列表
         * @throws IOException
         */
        public List<JstreeNode> getAllChildrenNode(String filePath,String relativePath) throws IOException {
            File file = new File(filePath+relativePath);
            List<JstreeNode> list = new LinkedList<>();
            try {
                //对于文件夹,我们要遍历它的下一级子节点
                File[] files = file.listFiles();
                JstreeNode jstreeNode;
                for (int i = 0; i < files.length; i++) {
                    //目录
                    if (files[i].isDirectory()) {
                        jstreeNode = new JstreeNode();
                        jstreeNode.setId(relativePath+files[i].getName() + "/");
                        jstreeNode.setText(files[i].getName());
                        jstreeNode.setHasChildren(true);
                        jstreeNode.setType(GlobalConfig.TYPE_FLODER);
                        list.add(jstreeNode);
                    }
                    //文件
                    else {
                        jstreeNode = new JstreeNode();
                        jstreeNode.setId(relativePath+files[i].getName());
                        jstreeNode.setText(files[i].getName());
                        jstreeNode.setHasChildren(false);
                        jstreeNode.setType(GlobalConfig.TYPE_FILE);
                        list.add(jstreeNode);
                    }
                }
                return list;
            } catch (Exception e) {
                // 请求失败时打印的异常的信息
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * NIO方式读取file文件为byte[]
         *
         * @param filename 文件名,要求包含文件绝对路径
         * @return 文件的byte[]形式
         * @throws IOException
         */
        public byte[] toByteArray(String filename) throws IOException {
            File file = new File(filename);
            /*
            Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。
            在使用FileChannel之前,必须先打开它。但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。
            FileChannel实例的size()方法将返回该实例所关联文件的大小。
             */
            FileChannel channel = null;
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(file);
                channel = fileInputStream.getChannel();
                //所分配的ByteBuffer的容量
                ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
                /*
                FileChannel.read()方法。该方法将数据从FileChannel读取到Buffer中。read()方法返回的int值表示了有多少字节被读到了Buffer中。
                如果返回-1,表示到了文件末尾。
                 */
                while ((channel.read(byteBuffer)) > 0) {
                    // do nothing
                    // System.out.println("reading");
                }
                return byteBuffer.array();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
    

    工具类,ZipUtils.java:

    import com.demo.fileTree.model.FileHandleResponse;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.*;
    import java.util.UUID;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
    import java.util.zip.ZipOutputStream;
    
    /**
     * 文件或者文件夹的压缩和解压缩,详细看java核心技术卷II,P27,
     * 注意,如果是更新项目,要将原来文件夹及文件夹中的内容全部删除,重新生成UUID及文件夹,在这里由于没有到数据库,就不执行这一步了
     *
     * @author xie
     * @version 1.0
     * @Date 2017/5/30
     */
    @Service
    public class ZipUtils {
    
        /** 头像图片的放置路径*/
        @Value("${zipPath.home}")
        private String ZipDir;
    
        /**
         * 获得图片存储路径
         * @return
         */
        public String getZipDir(){
            return ZipDir;
        }
    
        /**
         * 压缩文件-由于out要在递归外调用,所以封装一个方法
         * 压缩后的压缩文件的路径和命名,比如 File zipFile = new File("C:/home/myblog/project/32位UUID/test.zip"),
         *                 但注意解压缩后的文件夹的名字与压缩文件的名字不一定相同,test.zip只是压缩包的名字,
         *                 在这里我们将test.zip设为fileName.zip,放在32位UUID目录下面,和解压后的项目相同层次,
         *                 下载完成后也不删除,防止多人下载,服务器每次都要压缩文件
         *
         * @param filePath 要压缩的项目的路径
         * @throws IOException
         * @return FileHandleResponse 表示压缩结果实体对象
         */
        public static FileHandleResponse zipFiles(String filePath) throws IOException{
            FileHandleResponse fileHandleResponse = new FileHandleResponse();
    
            //将压缩文件和原项目放到相同目录下,并且相同命名,除了压缩文件以.zip结尾,但注意filePath以/结尾,要处理一下
            File zipFile = new File(filePath.substring(0,filePath.length()-1)+".zip");
            File noZipFile = new File(filePath);
            if(zipFile.exists()){
                fileHandleResponse.setMessage("压缩文件存在");
            }else if(!noZipFile.exists()){
                fileHandleResponse.setSuccess(0);
                fileHandleResponse.setMessage("请求文件夹或文件不存在!");
                return fileHandleResponse;
            }else{
                try {
                    //创建一个将压缩数据写出到指定的OutputStream的ZipOutputStream
                    ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
                    zipFiles(zipOutputStream, "", noZipFile);
                    zipOutputStream.close();
                    System.out.println("*****************压缩完毕*******************");
                    fileHandleResponse.setMessage("压缩成功");
                } catch (Exception e) {
                    fileHandleResponse.setSuccess(0);
                    fileHandleResponse.setMessage("服务器异常");
                    e.printStackTrace();
                    return fileHandleResponse;
                }
            }
            fileHandleResponse.setSuccess(1);
            fileHandleResponse.setUrl(zipFile.getAbsolutePath());
            return fileHandleResponse;
        }
    
        /**
         * 压缩文件,
         * 如果是目录,则对目录里的文件重新调用ZipFiles方法,一级目录一级目录的压缩
         *
         * @param zipOutputStream 压缩文件输出流
         * @param fileParentPath 压缩文件的上级目录
         * @param srcFiles 要压缩的文件,可以压缩1到多个文件,通过写数组的方式或者一个个写到参数列表里面
         */
        public static void zipFiles(ZipOutputStream zipOutputStream,String fileParentPath,File... srcFiles){
            //将目录中的1个或者多个置换为/,因为在windows目录下,以或者\为文件目录分隔符,linux却是/
            if(fileParentPath!=""){
                fileParentPath = fileParentPath.replaceAll("\+", "/");
                if(!fileParentPath.endsWith("/")){
                    fileParentPath+="/";
                }
            }
            byte[] bytes = new byte[4096];
            try {
                /*
                希望放入zip文件的每一项,都应该创建一个ZipEntry对象,然后将文件名传递给ZipEntry的构造器,它将设置文件日期,解压缩方法等参数,
                并且需要调用putNextEntry方法来开始写出新文件,并将文件数据放松到zip流中,当完成时,需要调用closeEntry方法。所有文件都重复这一过程。
                 */
    
                for(int i=0;i<srcFiles.length;i++){
                    //对于目录,递归
                    if(srcFiles[i].isDirectory()){
                        File[] files = srcFiles[i].listFiles();
                        String srcPath = srcFiles[i].getName();
                        srcPath = srcPath.replaceAll("\+", "/");
                        if(!srcPath.endsWith("/")){
                            srcPath+="/";
                        }
                        zipOutputStream.putNextEntry(new ZipEntry(fileParentPath+srcPath));
                        zipFiles(zipOutputStream,fileParentPath+srcPath,files);
                    }
                    //对于文件,发送到ZIP流中,利用4KB的缓冲区,可以考虑使用BufferedInputStream()流过滤器
                    else{
                        FileInputStream fileInputStream = new FileInputStream(srcFiles[i]);
                        zipOutputStream.putNextEntry(new ZipEntry(fileParentPath + srcFiles[i].getName()));
                        int len;
                        while((len=fileInputStream.read(bytes))>0){
                            zipOutputStream.write(bytes,0,len);
                        }
                        zipOutputStream.closeEntry();
                        fileInputStream.close();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 解压文件到指定目录
         * @param unZipPath 解压路径,比如C:\home\myblog\project\
         * @param fileName 解压后的文件名,一般命名为项目名,强制要求用户输入,并且保证不为空,
         *                 fileName的上层目录为一个随机生成的32位UUID,以保证项目名重复的依然可以保存到服务器
         * @param multipartFile 上传压缩文件
         *
         * @return FileHandleResponse 表示上传结果实体对象
         */
        @SuppressWarnings("rawtypes")
        public static FileHandleResponse unZipFiles(String unZipPath, String fileName, MultipartFile multipartFile)throws IOException{
            FileHandleResponse fileHandleResponse = new FileHandleResponse();
    
            String unZipRealPath = unZipPath +UUID.randomUUID().toString().replaceAll("-", "")+ "/"+fileName + "/";
            //如果保存解压缩文件的目录不存在,则进行创建,并且解压缩后的文件总是放在以fileName命名的文件夹下
            File unZipFile = new File(unZipRealPath);
            if (!unZipFile.exists()) {
                unZipFile.mkdirs();
            }
            //ZipInputStream用来读取压缩文件的输入流
            ZipInputStream zipInputStream = new ZipInputStream(multipartFile.getInputStream());
            //压缩文档中每一个项为一个zipEntry对象,可以通过getNextEntry方法获得,zipEntry可以是文件,也可以是路径,比如abc/test/路径下
            ZipEntry zipEntry;
            try {
                while ((zipEntry = zipInputStream.getNextEntry()) != null) {
                    String zipEntryName = zipEntry.getName();
                    //将目录中的1个或者多个置换为/,因为在windows目录下,以或者\为文件目录分隔符,linux却是/
                    String outPath = (unZipRealPath + zipEntryName).replaceAll("\+", "/");
                    //判断所要添加的文件所在路径或者
                    // 所要添加的路径是否存在,不存在则创建文件路径
                    File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    //判断文件全路径是否为文件夹,如果是,在上面三行已经创建,不需要解压
                    if (new File(outPath).isDirectory()) {
                        continue;
                    }
    
                    OutputStream outputStream = new FileOutputStream(outPath);
                    byte[] bytes = new byte[4096];
                    int len;
                    //当read的返回值为-1,表示碰到当前项的结尾,而不是碰到zip文件的末尾
                    while ((len = zipInputStream.read(bytes)) > 0) {
                        outputStream.write(bytes, 0, len);
                    }
                    outputStream.close();
                    //必须调用closeEntry()方法来读入下一项
                    zipInputStream.closeEntry();
                }
                zipInputStream.close();
                fileHandleResponse.setSuccess(1);
                fileHandleResponse.setMessage("解压完毕");
                fileHandleResponse.setUrl((unZipRealPath).replaceAll("\+", "/"));
                System.out.println("******************解压完毕********************");
    
            } catch (Exception e) {
                fileHandleResponse.setSuccess(0);
                fileHandleResponse.setMessage("服务器异常");
                e.printStackTrace();
                return fileHandleResponse;
            }
            return fileHandleResponse;
        }
    }
    

    对应的model层,FileHandleResponse.java:

    /**
     * 文件处理后回显提示的实体类
     *
     * @author xie
     * @version 1.0
     * @Date 2017/5/25
     */
    public class FileHandleResponse {
        /** 上传状态,0:失败,1:上传成功 */
        private int success;
    
        /** 图片上传提示信息,包括上传成功或上传失败及错误信息等 */
        private String message;
    
        /** 图片上传成功后返回的地址 */
        private String url;
    
        public int getSuccess() {
            return success;
        }
    
        public void setSuccess(int success) {
            this.success = success;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
    }
    

    JstreeNode.java

    /**
     * Jstree节点实体
     *
     * @author xie
     * @version 1.0
     * @Date 2017/5/31
     */
    public class JstreeNode {
        /** id并没有实际的意义,仅仅用于唯一标识节点,为了掌握节点之间的上下级关系,我们将id设为节点对file-path的相对路径 */
        private String id;
    
        /** 节点的显示名字,我们设为文件名 */
        private String text;
    
        /** 节点是否有孩子节点 */
        private boolean hasChildren;
    
        /** 节点类型,即文件还是文件夹,设置文件夹为0,文件为1 */
        private int type;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getText() {
            return text;
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        public boolean isHasChildren() {
            return hasChildren;
        }
    
        public void setHasChildren(boolean hasChildren) {
            this.hasChildren = hasChildren;
        }
    
        public int getType() {
            return type;
        }
    
        public void setType(int type) {
            this.type = type;
        }
    }
    
  • 相关阅读:
    css3动画
    jquery和js进行添加样式
    网站基本了解
    新闻内容显示样式去除
    网页中常用的js特效
    手机和电脑同时兼容的网站
    使用repeter绑定数据的最高效率的方法
    常识(其他)
    OI暑假集训游记
    动态规划归纳(基础篇)
  • 原文地址:https://www.cnblogs.com/xzwblog/p/6928371.html
Copyright © 2011-2022 走看看