java 预览zip_java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载
java壓縮包上傳,解壓,預(yù)覽(利用editor.md和Jstree實(shí)現(xiàn))和下載
實(shí)現(xiàn)功能:zip文件上傳,后臺(tái)自動(dòng)解壓,Jstree樹目錄(遍歷文件),editor.md預(yù)覽
采用Spring+SpringMVC+Maven+Jstree+editor.md實(shí)現(xiàn),主要功能:
zip壓縮文件的上傳
后臺(tái)自動(dòng)解壓
Jstree自動(dòng)獲取最上層目錄,每次僅僅會(huì)獲取當(dāng)前層的文件或者文件夾,然后點(diǎn)擊文件夾或者文件,通過(guò)ajax與服務(wù)器交換數(shù)據(jù),減輕檢索和數(shù)據(jù)傳輸壓力
后臺(tái)通過(guò)文件路徑遍歷文件夾
通過(guò)editor.md將文本代碼高亮顯示
圖片的解析預(yù)覽
總體項(xiàng)目目錄結(jié)構(gòu):
預(yù)覽:
點(diǎn)擊提交后:
并提供下載功能
1. 分析代碼
上傳壓縮包的html代碼,使用velocity模板渲染引擎:
上傳壓縮項(xiàng)目包提示:壓縮包內(nèi)請(qǐng)勿包含中文!
$(window).load(function() {
//當(dāng)鼠標(biāo)移出輸入框
$('#file-name').on('blur', function(){
var fileName = document.getElementById("file-name").value;
if(fileName==''){
$('.file-name-check').html('');
$('.file-name-check').append("請(qǐng)輸入項(xiàng)目名!")
}
});
$("#file-zip").bind("change",function(){
var imgArr = ["zip"];
if($(this).val() == "")
{
alert("請(qǐng)選擇文件!");
}
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==''){//當(dāng)項(xiàng)目名為空時(shí)
alert("請(qǐng)輸入項(xiàng)目名!");
return false;
}
if(document.getElementById("file-zip").value==''){//當(dāng)項(xiàng)目為空時(shí)
alert("請(qǐng)上傳項(xiàng)目!");
return false;
}
var formdata = new FormData(form);
$.ajax({
url:"/admin/file/zip/upload",
data: formdata,
type:"post",
//預(yù)期服務(wù)器返回的數(shù)據(jù)類型,自動(dòng)解析json返回值,不設(shè)置的話可能要執(zhí)行oResult = JSON.parse(oResult);進(jìn)行解析
dataType:"json",
//默認(rèn)值: true。默認(rèn)情況下,通過(guò)data選項(xiàng)傳遞進(jìn)來(lái)的數(shù)據(jù),如果是一個(gè)對(duì)象(技術(shù)上講只要不是字符串),
// 都會(huì)處理轉(zhuǎn)化成一個(gè)查詢字符串,以配合默認(rèn)內(nèi)容類型 "application/x-www-form-urlencoded"。如果要發(fā)送 DOM 樹信息或其它不希望轉(zhuǎn)換的信息,請(qǐng)?jiān)O(shè)置為 false。
processData: false,
//contentType: false,避免 JQuery 對(duì)data操作,可能失去分界符,而使服務(wù)器不能正常解析文件。
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表示不論成功還是失敗,會(huì)執(zhí)行的函數(shù),
// //但是在低版本jquery不兼容,是高版本jquery推薦
// if(oResult.success==1){
// window.location.href="/";
// alert(oResult.message);
// }else{
// alert(oResult.message);
// }
// }).fail(function () {
// alert('出現(xiàn)錯(cuò)誤,請(qǐng)重試');
// });
})
});
預(yù)覽自動(dòng)解壓后文件夾的html代碼,使用velocity模板渲染引擎:
項(xiàng)目展示Search
下載
##支持markdown快速解析
##支持代碼高亮
對(duì)應(yīng)的JS文件,file-node.js:
$(function() {
var filePath = document.getElementById("filePathRem").value;
//注意這里面只能處理尋找文件夾的子文件或者子文件夾事件,可以把文件的讀取寫到 $('#container').on("changed.jstree", function (e, data)函數(shù)中
$('#container').jstree({
'core': {
'data':
//node為點(diǎn)擊的節(jié)點(diǎn),cd為輸出結(jié)果的函數(shù)
function (node, cb) {
var formdata = new FormData();
formdata.append("file-path",filePath);
formdata.append("id",node.id);
//通過(guò)表單對(duì)象上傳文件或者數(shù)據(jù),設(shè)置
// processData: false,表示不要對(duì)data參數(shù)進(jìn)行序列化處理
//contentType: false,避免 JQuery 對(duì)data操作,可能失去分界符,而使服務(wù)器不能正常解析文件。
$.ajax({
//不要用get方法,因?yàn)?在瀏覽器中有特殊含義,
// #代表網(wǎng)頁(yè)中的一個(gè)位置。其右面的字符,就是該位置的標(biāo)識(shí)符。比如,http://www.example.com/index.html#print就代表網(wǎng)頁(yè)index.html的print位置。
// 瀏覽器讀取這個(gè)URL后,會(huì)自動(dòng)將print位置滾動(dòng)至可視區(qū)域。
//并且在發(fā)送的請(qǐng)求中,自動(dòng)忽略#,而首次打開頁(yè)面的第一次請(qǐng)求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為文件夾,即默認(rèn),1為文件
"types" : {
0 : {
"icon" : "glyphicon glyphicon-folder",
"valid_children" : []
},
1 : {
"icon" : "glyphicon glyphicon-file"
}
},
//搜索功能插件和類別插件,以對(duì)文件夾和文有不同的圖標(biāo)
"plugins" : ["search","types"]
});
//上面的表單s和本函數(shù)都用于搜索,模糊搜索,不區(qū)分大小寫
$("#s").submit(function(e) {
e.preventDefault();
$("#container").jstree(true).search($("#q").val());
});
//注意changed與click的區(qū)別,前者只要狀態(tài)不變,點(diǎn)擊多少次都加載一次,后者每次點(diǎn)擊都重新加載
$('#container').on("changed.jstree", function (e, data) {
// console.log("The selected nodes are:");
// //顯示被選擇節(jié)點(diǎn)id編號(hào)
// console.log(data.selected);
// //顯示被選擇節(jié)點(diǎn)的命名
// console.log(data.node.text);
var name=String(data.selected);
//如果包含.則為請(qǐng)求文件
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) {
//首先把頁(yè)面中的可能存在的圖片清空
document.getElementById("image-panel").innerHTML ='';
//由于editor.md每次更新內(nèi)容之后都會(huì)將刪除,那么每次更新前都需要添加
document.getElementById("markdown-editor").innerHTML='';
document.getElementById("append-test").value="```\n"+oResult.fileContent+"\n```";
//用于將markdown文本轉(zhuǎn)化為html格式
editormd.markdownToHTML("markdown-editor", {
});
} else {
alert(oResult.result.message);
}
}
})
}else { //對(duì)于圖片,我們要顯示為圖片,而不是文本的字符流
document.getElementById("markdown-editor").innerHTML='';
document.getElementById("image-panel").innerHTML = '';
document.getElementById("img-circle").src = "/admin/file/zip/image.action?file-path="+filePath+"&id="+name;
}
}
});
//判斷請(qǐng)求文件是否是圖片,僅支持常用類型
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;
}
});
對(duì)應(yīng)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;
/**
* 實(shí)現(xiàn)項(xiàng)目zip壓縮包的上傳,自動(dòng)解壓,解壓后的預(yù)覽,包括文本和字符串,項(xiàng)目的壓縮下載,
* 由于java.util.zip包不支持漢字的問(wèn)題,在項(xiàng)目壓縮包內(nèi)請(qǐng)勿包含中文文件名,但是在頁(yè)面中的項(xiàng)目名可以起名為中文,
* 可以用org.apache.tools.zip壓縮/解壓縮zip文件,解決中文亂碼問(wèn)題。
*
* @author xie
* @version 1.0
* @Date 2017/5/26
*/
@Controller
public class FileController {
@Autowired
FileService fileService;
/**
* 主頁(yè)
* @return
*/
@RequestMapping(path = {"/"}, method = {RequestMethod.GET, RequestMethod.POST})
public String index() {
return "upload_zip";
}
/**
* 上傳壓縮zip項(xiàng)目文件
* @param file zip壓縮文件
* @param fileName 項(xiàng)目的命名,我們將解壓縮的文件放到以項(xiàng)目名命名的文件夾內(nèi),為了保證項(xiàng)目名重復(fù)的也可以上傳,項(xiàng)目名文件夾外部還有一個(gè)32位UUID命名的文件夾,
* 只不過(guò)取出項(xiàng)目時(shí)沒有顯示
* @return 結(jié)果的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("服務(wù)器異常!");
fileHandleResponse.setUrl(null);
return JSON.toJSONString(fileHandleResponse);
}
}
/**
* 展示上傳的zip項(xiàng)目解壓縮后的文件結(jié)構(gòu)
* @param filePath 項(xiàng)目的路徑,比如,C:\home\myblog\project\2d76c7aa844b4585a53d982d205099e2\123\其中123為項(xiàng)目名,
* @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不同,
// 包含文件名,我們提取出來(lái),作為fileName,分隔符可能為/或\或\\,其中\(zhòng)要轉(zhuǎn)意為\\
String fileName = filePath.split("\\|\\\\|/")[filePath.split("\\|\\\\|/").length-1];
model.addAttribute("fileName",fileName);
return "show_zip";
}
/**
* 項(xiàng)目展示頁(yè)面
* @param filePath 項(xiàng)目路徑
* @param relativePath 節(jié)點(diǎn)相比項(xiàng)目路徑的相對(duì)路徑,比如項(xiàng)目路徑:
* C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
* 那么節(jié)點(diǎn)路徑src/main/java/表示
* C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/src/main/java/
* @return 對(duì)于文件,返回字符內(nèi)容的json字符串,對(duì)于文件夾,返回文件夾的下一級(jí)所有子文件和子文件夾,其實(shí)若文件是圖片,我們?cè)谙旅娴膅etImage()方法中處理
*/
@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("#")) { //表示第一次打開頁(yè)面的請(qǐng)求,relativePath為#,沒什么意義,設(shè)為空字符串
relativePath = "";
}
File file = new File(filePath+relativePath);
//如果請(qǐng)求路徑存在,即文件或者目錄存在
if (file.exists()) {
//分為文件或者文件夾兩種情況
if (file.isFile()) {
BufferedReader bufferedReader;
try {
StringBuilder stringBuilder = new StringBuilder();
//將字節(jié)流向字符流的轉(zhuǎn)換,并創(chuàng)建字符流緩沖區(qū)
bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
// 每次讀入一行
String read;
//每讀入一行,要加一個(gè)換行符
String lineText="\n";
while ((read = bufferedReader.readLine()) != null) {
stringBuilder.append(read+lineText);
}
bufferedReader.close();
fileHandleResponse.setSuccess(1);
fileHandleResponse.setMessage("請(qǐng)求成功!");
model.addAttribute("result", fileHandleResponse);
model.addAttribute("fileContent", stringBuilder.toString());
return JSON.toJSONString(model);
} catch (Exception e1) {
e1.printStackTrace();
}
} else {
List 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("請(qǐng)求成功!");
model.addAttribute("result", fileHandleResponse);
//最好不要直接傳遞list,前端不可以很好的解析
model.addAttribute("array", jsonArray);
return JSON.toJSONString(model);
}
} else { //如果請(qǐng)求路徑不存在
fileHandleResponse.setSuccess(0);
fileHandleResponse.setMessage("請(qǐng)求路徑不存在!");
model.addAttribute("result",fileHandleResponse);
return JSON.toJSONString(model);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 將項(xiàng)目壓縮后以字節(jié)流的方式發(fā)送
* @param filePath 項(xiàng)目路徑
* @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不同,
// 包含文件名,我們提取出來(lái),作為fileName,分隔符可能為/或\或\\,其中\(zhòng)要轉(zhuǎn)意為\\
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();
}
}
/**
* 按照?qǐng)D片路徑查找圖片
* @param filePath 項(xiàng)目路徑
* @param relativePath 節(jié)點(diǎn)相比項(xiàng)目路徑的相對(duì)路徑,比如項(xiàng)目路徑:
* C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
* 那么節(jié)點(diǎn)路徑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();
}
}
}
對(duì)應(yīng)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;
/**
* 壓縮文件上傳,并且解壓縮后放到服務(wù)器響應(yīng)目錄下,
* 為什么不直接放壓縮包,因?yàn)閯e人看一次,需要解壓縮一次,也很浪費(fèi)系統(tǒng)資源
*
* @author xie
* @version 1.0
* @Date 2017/5/27
*/
@Service
public class FileService {
@Autowired
ZipUtils zipUtils;
/**
* 默認(rèn)上傳zip壓縮格式
* @param file 上傳的文件
* @return 上傳的結(jié)果UploadResponse對(duì)象
* @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) {
// 請(qǐng)求失敗時(shí)打印的異常的信息
fileHandleResponse = new FileHandleResponse();
fileHandleResponse.setSuccess(0);
fileHandleResponse.setMessage("服務(wù)器異常!");
return fileHandleResponse;
}
}
/**
* 下載壓縮后的項(xiàng)目文件
*
* @param filePath 項(xiàng)目路徑
* @return 文件處理結(jié)果實(shí)體,其中url表示項(xiàng)目壓縮后的路徑
* @throws IOException
*/
public FileHandleResponse downloadFileZip(String filePath) throws IOException {
FileHandleResponse fileHandleResponse;
try {
fileHandleResponse = zipUtils.zipFiles(filePath);
return fileHandleResponse;
} catch (Exception e) {
// 請(qǐng)求失敗時(shí)打印的異常的信息
fileHandleResponse = new FileHandleResponse();
fileHandleResponse.setSuccess(0);
fileHandleResponse.setMessage("服務(wù)器異常!");
return fileHandleResponse;
}
}
/**
* 返回某一結(jié)點(diǎn)(即文件夾)的下一級(jí)所有子節(jié)點(diǎn),注意這里輸入的不是具體文件或者不存在的路徑,是已經(jīng)判定存在的文件夾路徑,
* 如果是請(qǐng)求具體文件或者不存在的路徑,在上一層controller層就應(yīng)該將文件內(nèi)容讀取并返回或者返回錯(cuò)誤信息
*
* @param filePath 項(xiàng)目路徑
* @param relativePath 節(jié)點(diǎn)相比項(xiàng)目路徑的相對(duì)路徑,比如項(xiàng)目路徑:
* C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/
* 那么節(jié)點(diǎn)路徑src/main/java/表示
* C:/home/myblog/project/dccb182a7ded477483362ce46be1eb5c/123/src/main/java/
* 但是由于files[i].getName()只會(huì)獲得abc這樣的單層目錄名或者abc.java這樣的文件名,因此我們要設(shè)置下一級(jí)的相對(duì)路徑為;
* relativePath+files[i].getName()(如果是路徑,還要包含/)
*
* @return 所有子節(jié)點(diǎn)的列表
* @throws IOException
*/
public List getAllChildrenNode(String filePath,String relativePath) throws IOException {
File file = new File(filePath+relativePath);
List list = new LinkedList<>();
try {
//對(duì)于文件夾,我們要遍歷它的下一級(jí)子節(jié)點(diǎn)
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) {
// 請(qǐng)求失敗時(shí)打印的異常的信息
e.printStackTrace();
}
return null;
}
/**
* NIO方式讀取file文件為byte[]
*
* @param filename 文件名,要求包含文件絕對(duì)路徑
* @return 文件的byte[]形式
* @throws IOException
*/
public byte[] toByteArray(String filename) throws IOException {
File file = new File(filename);
/*
Java NIO中的FileChannel是一個(gè)連接到文件的通道。可以通過(guò)文件通道讀寫文件。FileChannel無(wú)法設(shè)置為非阻塞模式,它總是運(yùn)行在阻塞模式下。
在使用FileChannel之前,必須先打開它。但是,我們無(wú)法直接打開一個(gè)FileChannel,需要通過(guò)使用一個(gè)InputStream、OutputStream或RandomAccessFile來(lái)獲取一個(gè)FileChannel實(shí)例。
FileChannel實(shí)例的size()方法將返回該實(shí)例所關(guān)聯(lián)文件的大小。
*/
FileChannel channel = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
channel = fileInputStream.getChannel();
//所分配的ByteBuffer的容量
ByteBuffer byteBuffer = ByteBuffer.allocate((int) channel.size());
/*
FileChannel.read()方法。該方法將數(shù)據(jù)從FileChannel讀取到Buffer中。read()方法返回的int值表示了有多少字節(jié)被讀到了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;
/**
* 文件或者文件夾的壓縮和解壓縮,詳細(xì)看java核心技術(shù)卷II,P27,
* 注意,如果是更新項(xiàng)目,要將原來(lái)文件夾及文件夾中的內(nèi)容全部刪除,重新生成UUID及文件夾,在這里由于沒有到數(shù)據(jù)庫(kù),就不執(zhí)行這一步了
*
* @author xie
* @version 1.0
* @Date 2017/5/30
*/
@Service
public class ZipUtils {
/** 頭像圖片的放置路徑*/
@Value("${zipPath.home}")
private String ZipDir;
/**
* 獲得圖片存儲(chǔ)路徑
* @return
*/
public String getZipDir(){
return ZipDir;
}
/**
* 壓縮文件-由于out要在遞歸外調(diào)用,所以封裝一個(gè)方法
* 壓縮后的壓縮文件的路徑和命名,比如 File zipFile = new File("C:/home/myblog/project/32位UUID/test.zip"),
* 但注意解壓縮后的文件夾的名字與壓縮文件的名字不一定相同,test.zip只是壓縮包的名字,
* 在這里我們將test.zip設(shè)為fileName.zip,放在32位UUID目錄下面,和解壓后的項(xiàng)目相同層次,
* 下載完成后也不刪除,防止多人下載,服務(wù)器每次都要壓縮文件
*
* @param filePath 要壓縮的項(xiàng)目的路徑
* @throws IOException
* @return FileHandleResponse 表示壓縮結(jié)果實(shí)體對(duì)象
*/
public static FileHandleResponse zipFiles(String filePath) throws IOException{
FileHandleResponse fileHandleResponse = new FileHandleResponse();
//將壓縮文件和原項(xiàng)目放到相同目錄下,并且相同命名,除了壓縮文件以.zip結(jié)尾,但注意filePath以/結(jié)尾,要處理一下
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("請(qǐng)求文件夾或文件不存在!");
return fileHandleResponse;
}else{
try {
//創(chuàng)建一個(gè)將壓縮數(shù)據(jù)寫出到指定的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("服務(wù)器異常");
e.printStackTrace();
return fileHandleResponse;
}
}
fileHandleResponse.setSuccess(1);
fileHandleResponse.setUrl(zipFile.getAbsolutePath());
return fileHandleResponse;
}
/**
* 壓縮文件,
* 如果是目錄,則對(duì)目錄里的文件重新調(diào)用ZipFiles方法,一級(jí)目錄一級(jí)目錄的壓縮
*
* @param zipOutputStream 壓縮文件輸出流
* @param fileParentPath 壓縮文件的上級(jí)目錄
* @param srcFiles 要壓縮的文件,可以壓縮1到多個(gè)文件,通過(guò)寫數(shù)組的方式或者一個(gè)個(gè)寫到參數(shù)列表里面
*/
public static void zipFiles(ZipOutputStream zipOutputStream,String fileParentPath,File... srcFiles){
//將目錄中的1個(gè)或者多個(gè)\置換為/,因?yàn)樵趙indows目錄下,以\或者\(yùn)\為文件目錄分隔符,linux卻是/
if(fileParentPath!=""){
fileParentPath = fileParentPath.replaceAll("\\+", "/");
if(!fileParentPath.endsWith("/")){
fileParentPath+="/";
}
}
byte[] bytes = new byte[4096];
try {
/*
希望放入zip文件的每一項(xiàng),都應(yīng)該創(chuàng)建一個(gè)ZipEntry對(duì)象,然后將文件名傳遞給ZipEntry的構(gòu)造器,它將設(shè)置文件日期,解壓縮方法等參數(shù),
并且需要調(diào)用putNextEntry方法來(lái)開始寫出新文件,并將文件數(shù)據(jù)放松到zip流中,當(dāng)完成時(shí),需要調(diào)用closeEntry方法。所有文件都重復(fù)這一過(guò)程。
*/
for(int i=0;i
//對(duì)于目錄,遞歸
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);
}
//對(duì)于文件,發(fā)送到ZIP流中,利用4KB的緩沖區(qū),可以考慮使用BufferedInputStream()流過(guò)濾器
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 解壓后的文件名,一般命名為項(xiàng)目名,強(qiáng)制要求用戶輸入,并且保證不為空,
* fileName的上層目錄為一個(gè)隨機(jī)生成的32位UUID,以保證項(xiàng)目名重復(fù)的依然可以保存到服務(wù)器
* @param multipartFile 上傳壓縮文件
*
* @return FileHandleResponse 表示上傳結(jié)果實(shí)體對(duì)象
*/
@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 + "/";
//如果保存解壓縮文件的目錄不存在,則進(jìn)行創(chuàng)建,并且解壓縮后的文件總是放在以fileName命名的文件夾下
File unZipFile = new File(unZipRealPath);
if (!unZipFile.exists()) {
unZipFile.mkdirs();
}
//ZipInputStream用來(lái)讀取壓縮文件的輸入流
ZipInputStream zipInputStream = new ZipInputStream(multipartFile.getInputStream());
//壓縮文檔中每一個(gè)項(xiàng)為一個(gè)zipEntry對(duì)象,可以通過(guò)getNextEntry方法獲得,zipEntry可以是文件,也可以是路徑,比如abc/test/路徑下
ZipEntry zipEntry;
try {
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
String zipEntryName = zipEntry.getName();
//將目錄中的1個(gè)或者多個(gè)\置換為/,因?yàn)樵趙indows目錄下,以\或者\(yùn)\為文件目錄分隔符,linux卻是/
String outPath = (unZipRealPath + zipEntryName).replaceAll("\\+", "/");
//判斷所要添加的文件所在路徑或者
// 所要添加的路徑是否存在,不存在則創(chuàng)建文件路徑
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
}
//判斷文件全路徑是否為文件夾,如果是,在上面三行已經(jīng)創(chuàng)建,不需要解壓
if (new File(outPath).isDirectory()) {
continue;
}
OutputStream outputStream = new FileOutputStream(outPath);
byte[] bytes = new byte[4096];
int len;
//當(dāng)read的返回值為-1,表示碰到當(dāng)前項(xiàng)的結(jié)尾,而不是碰到zip文件的末尾
while ((len = zipInputStream.read(bytes)) > 0) {
outputStream.write(bytes, 0, len);
}
outputStream.close();
//必須調(diào)用closeEntry()方法來(lái)讀入下一項(xiàng)
zipInputStream.closeEntry();
}
zipInputStream.close();
fileHandleResponse.setSuccess(1);
fileHandleResponse.setMessage("解壓完畢");
fileHandleResponse.setUrl((unZipRealPath).replaceAll("\\+", "/"));
System.out.println("******************解壓完畢********************");
} catch (Exception e) {
fileHandleResponse.setSuccess(0);
fileHandleResponse.setMessage("服務(wù)器異常");
e.printStackTrace();
return fileHandleResponse;
}
return fileHandleResponse;
}
}
對(duì)應(yīng)的model層,FileHandleResponse.java:
/**
* 文件處理后回顯提示的實(shí)體類
*
* @author xie
* @version 1.0
* @Date 2017/5/25
*/
public class FileHandleResponse {
/** 上傳狀態(tài),0:失敗,1:上傳成功 */
private int success;
/** 圖片上傳提示信息,包括上傳成功或上傳失敗及錯(cuò)誤信息等 */
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節(jié)點(diǎn)實(shí)體
*
* @author xie
* @version 1.0
* @Date 2017/5/31
*/
public class JstreeNode {
/** id并沒有實(shí)際的意義,僅僅用于唯一標(biāo)識(shí)節(jié)點(diǎn),為了掌握節(jié)點(diǎn)之間的上下級(jí)關(guān)系,我們將id設(shè)為節(jié)點(diǎn)對(duì)file-path的相對(duì)路徑 */
private String id;
/** 節(jié)點(diǎn)的顯示名字,我們?cè)O(shè)為文件名 */
private String text;
/** 節(jié)點(diǎn)是否有孩子節(jié)點(diǎn) */
private boolean hasChildren;
/** 節(jié)點(diǎn)類型,即文件還是文件夾,設(shè)置文件夾為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;
}
}
總結(jié)
以上是生活随笔為你收集整理的java 预览zip_java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 计算机网络—一个自治系统有5个局域网,其
- 下一篇: java zookeeper 使用场景_