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="```\n"+oResult.fileContent+"\n```";
                            //用於將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:\home\myblog\project\2d76c7aa844b4585a53d982d205099e2\123\其中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:\home\myblog\project\2d76c7aa844b4585a53d982d205099e2\123\,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="\n";
                        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:\home\myblog\project\2d76c7aa844b4585a53d982d205099e2\123.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;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM