文件夾管理工具(MVC+zTree+layer)


文件夾管理工具(MVC+zTree+layer)(附源碼)

 

 

寫在前

 

         之前寫了一篇關於 文件夾與文件的操作的文章  操作文件方法簡單總結(File,Directory,StreamReader,StreamWrite ) 

把常用的對於文件與文件夾的操作總結了一下,在文章的回復中有提到 遺漏的方法,並建議做一個文檔管理工具 ,一細想是可以啊,之后就開始構思,最后大至需求如下,

因為都是下班后在家中花時間做的,時間有點短,有BUG是正常的,希望大家諒解,再一個對windows的文件權限,幾乎是0接觸所以把權限處理留到了后期,如果大家有關於文件夾權限處理的好文章,希望大家給我留言,好的東西需要分享,本篇會寫一下幾個功能點的思路與做法,算是對自己的總結,如果對本項目有興趣的朋友請 狂點這里

最后如果您覺得,有什么地方做得不夠好,或者您有什么建議 與 意見 請一定要指出! 在此非常感謝!  

 

 

一 工具功能點

 一 展示
1 以樹形展示單個磁盤內所有文件夾子文件夾,與文件名
2 支持子文件夾的層級展開
3 右側支持文本文檔的預覽(二期)
4 若是圖片支持預覽(二期)
 
二 操作
 1 支持指定目錄下的文件夾新增,(右鍵)
 2 支持指定目錄下的文件夾刪除(右鍵)
 3 支持指定目錄下的文件夾,與文件的重命名((右鍵))
 4 支持指定目錄下的文件夾與文件移動到另一指定的文件夾(托拽)
 5 支持指定目錄下的文件夾批量刪除
 6 支持指定目錄下的文件搜索 (只要文件名匹配即可)(二期)
 7 文件夾與文件的訪問權限處理
 
以上就是大至簡單的需求,然后根據需求,選擇了以下幾個小插件
zTree:樹形插件 個人覺得很不錯,國內的插件,API文檔解釋很詳細,至少我從來沒用過樹形插件的,比較容易的就上手了。  zTree插件地址
Layer:彈出層插件,使用簡單,效果好,拿來即用    Layer插件地址
 

二 工具基本介紹

 
先上幾張圖   新建文件夾,文件夾或文件重命名
          
 
刪除文件或文件夾(支持批量刪除)
 
 
以拖拽形式批量移動文件夾
 
 
 
 
 

三 功能實現講解

 
3.1 加載文件夾樹
 
  根據zTree需要的參數配置就行了,具體參數請大家看zTree的API吧,着重寫一下加載時遇到的坑
實現思路:遍歷指定路徑下第一層所有文件夾與文件並組成對象序列化成Json並返回Json數據給zTree綁定
 
    剛開始想着一次性把所有的文件與文件夾都加載出來,然后用遞歸實現,但是因為我定義了一個類來保存文件的信息,而且是以嵌套的形式存的,這樣的做法初看覺得
很有層次感到寫遞歸時,卻把自己坑了,也主要是以前沒認真寫過遞歸,導致這一方法糾結了一個晚上再加上午的一點時間,終於遞歸出來了,
然后一試驗,瀏覽器直接死了,數量太大嵌套的太深,加載一次至少要40秒以上,於是果斷放棄了,改為每次只加載一層
以下為保存數據的類,與加載的方法 , 這里只列出部分代碼,具體代碼在源碼中都可找到
 
類設計
  View Code

public class zTreeModel
{
public zTreeModel()
{
_isPrent = false;
_children = new List<zTreeModel>();
}

private bool _isPrent ;
private List<zTreeModel> _children = null;

/// <summary>
///根結點名字
/// </summary>
public string rootFullName { get; set; }

/// <summary>
/// 父結點名稱
/// </summary>
public string pName { get; set; }
/// <summary>
/// 父結點全路徑名稱
/// </summary>
public string pFullName { get; set; }

/// <summary>
/// 當前結點全路徑名稱
/// </summary>
public string fullName { get; set; }

/// <summary>
/// 當前結點名稱
/// </summary>
public string name { get; set; }

/// <summary>
/// 是否為父結點
/// </summary>
public bool isParent {
get { return _isPrent; }
set { _isPrent = value; }
}
/// <summary>
/// 是否為頂層節點
/// </summary>
public bool topNode { get; set; }

/// <summary>
/// 是否為文件 默認為False
/// </summary>
public bool isFile { get; set; }

/// <summary>
/// 是否打開
/// </summary>
public bool open { get; set; }

/// <summary>
/// 子結點
/// </summary>
public List<zTreeModel> children
{
get { return _children; }
set { _children = value; }
}
}

JavaScript

  View Code

var zTreeObj,
setting = {
view: {
selectedMulti: true,
showLine: true
},
edit: {
drag: {
isMove: true,
inner:true//拖拽后為同級節點,
},//托拽操作配置
enable: true,//節點可編輯
showRemoveBtn: false,
showRenameBtn: false,
editNameSelectAll: true
},
data: {
keep: {
parent:true //刪除子節點后父節點不變為葉子節點
},

},
async: {
enable: true,
autoParam:["fullName"],
url: "/FileManager/GetSingleLevelNodes",
type: "POST",

},
treeNode: {
checked:true

},
callback:
{
// beforeExpand: zTreeBeforeExpand,//展開節點前的回調方法

beforeRename: zTreeBeforeRename,//重命名之前的回調方法
onRename: zTreeOnRename,//重命名

beforeRemove: zTreeBeforeRemove,//刪除前回調方法
onRemove: zTreeRemove,//刪除

beforeRightClick: zTreeBeforeRightClick,//右鍵方法前的回調
onRightClick: zTreeOnRightClick,//右鍵方法

beforeDrop: zTreeBeforeDrop,//用於捕獲節點拖拽操作結束之前的事件回調函數,並且根據返回值確定是否允許此拖拽操作.如果返回 false,zTree 將恢復被拖拽的節點,也無法觸發 onDrop 事件回調函數
onDrop: zTreeOnDrop,//拖拽操作結束的事件
beforeDrag: zTreeBeforeDrag//托拽前的方法

}
};

var zTreeNodes = "";
$(function () {
ReLoadTree();
});

function ReLoadTree() {
$.ajax({
url: "/FileManager/GetDefaultFiles",
type: "POST",
async: false,
dataType: "json",
success: function (data) {
zTreeNodes = data;
}
});
zTreeObj = $.fn.zTree.init($("#tree"), setting, zTreeNodes);

}

C#

  View Code

public List<zTreeModel> GetDefaultFiles(string path)
{
zTreeModel treeModel = null;
List<zTreeModel> treeModelList = new List<zTreeModel>();
if (Directory.Exists(path))
{
//獲取子目錄
DirectoryInfo directory = new DirectoryInfo(path);

try
{
var folders = directory.GetDirectories();
//遍歷路徑下文件夾
foreach (var folder in folders)
{
treeModel = new zTreeModel();
treeModel.pName = folder.Parent == null ? " " : folder.Parent.Name;
treeModel.pFullName = folder.Parent == null ? " " : folder.Parent.FullName;
treeModel.rootFullName = folder.Root.FullName;
treeModel.name = folder.Name;
treeModel.fullName = folder.FullName;
treeModel.isParent = true;
treeModelList.Add(treeModel);
}

}
catch (UnauthorizedAccessException ex)//調用方沒有所要求的權限。
{
return null;
}

//獲取路徑下文件
DirectoryInfo fileDirectory = new DirectoryInfo(path);

try
{
var files = fileDirectory.GetFiles();
foreach (var file in files)
{
treeModel = new zTreeModel();
treeModel.pName = file.Directory == null ? "" : file.Directory.Name;
treeModel.pFullName = file.DirectoryName;
treeModel.rootFullName = file.Directory == null ? "" : file.Directory.Root.FullName;
treeModel.name = file.Name;
treeModel.fullName = file.FullName;
treeModel.isFile = true;
treeModelList.Add((treeModel));
}

}
catch (UnauthorizedAccessException ex) //調用方沒有所要求的權限。
{
return null;
}
}

return treeModelList;

}

 

3.2 新增文件夾節點

實現思路:彈出框寫文件夾名,然后前台組裝新節點的json數據,並把新節點對象傳到Action中反序列化成對象,根據節點屬性創建文件夾,如果已存在相同文件名則提示,否則正常創建

javascript 

  View Code

//新增節點
function zTreeAddNode() {
var zTreeObj = $.fn.zTree.getZTreeObj("tree");
var parent = zTreeObj.getSelectedNodes()[0]; //把選中的節點當做父節點
if (parent == undefined) {
layer.alert('給新孩子找個父節點啊~~', 8);
return false;
} else if (parent.isParent == false) {
layer.alert('親~只能選文件夾哦~', 8);
return false;
}

ShowDialog(parent, zTreeObj);
}

function ShowDialog(parent, zTreeObj) {

$.layer({
shade: [0.5, '#000', true], //遮罩
dialog: {
msg: '文件夾名:<input type="text" id="folderName"/>',
title: ['新建文件夾', true],
btns: 2,
type: 1,
area: ['503px', '395px'],
btn: ['添加', '取消'],
yes: function(index) {

var name = $("#folderName").val();
if (name == "") {
layer.alert('啊喂~還沒寫名字呢~', 8);
return false;
}
//拼裝新節點對象
var nodes = { "fullName": parent.fullName + " \\ " + name, "name": name, "pName": parent.pName, "pFullName": parent.pFullName, "isParent": true };
$.ajax({
url: "/FileManager/AddNode",
type: "POST",
data: {
"node": JSON.stringify(nodes)
},
dataType: "json",
success: function(data) {
if (!data.Status) {
layer.alert(data.Message, 8);
layer.close(index);
return false;
}
zTreeObj.addNodes(parent, nodes);
layer.close(index);
}
});

},
no: function(index) {
layer.close(index);
}
}
});

}

C# 

  View Code

public OperationResult CreateFolder(zTreeModel zTree, OperationResult operation)
{
DirectoryInfo directory = new DirectoryInfo(zTree.fullName);
if (directory.Exists)
{
operation.Status = false;
operation.Message = "文件夾已存在了哦~";
}
else
{
directory.Create();
}
return operation;
}

 

 

3.3  刪除樹上文件夾節點

     思路:取到選中節點,再取節點fullName 屬性,(此屬性保存了文件的全路徑),把fullName異步傳入Action后調用方法,遞歸刪除文件夾,遞歸的方式是先遍歷到最深層的子文件夾如果有遇到不可訪問的文件則刪除操作不能繼續進行,並提示用戶不能刪除的文件夾名,如果都可訪問則從最深層的子文件夾開始遞歸刪除,代碼如下

JavaScript

  View Code

//刪除節點
function zTreeRemove() {
var treeObj = $.fn.zTree.getZTreeObj("tree");
var node = treeObj.getSelectedNodes();
var isParents = false;
$.each(node, function(index, item) {
if (item.isParent) {
isParents = true;
}
});
if (isParents) {
var meg = "確定要把文件夾中所有文件都刪了嗎?不能恢復的哦~也不能怪我哦~";
deleteNodes(node, meg);
} else {
var meg = "確定要刪除所以選中的文件嗎?不能恢復的哦~";
deleteNodes(node, meg);
}
}
function deleteNodes(nodes, meg) {
$.layer({
shade: [0.5, '#000', true], //遮罩
dialog: {
msg: meg,
title: '刪除文件夾',
btns: 2,
type: 1,
area: ['503px', '395px'],
btn: ['刪除', '取消'],
yes: function(index) {
$.ajax({
url: "/FileManager/DeleteNode",
type: "POST",
data: { "node": JSON.stringify(nodes) },
dataType: "json",
success: function(data) {
if (!data.Status) {
layer.alert(data.Message, 8);
} else {
var treeObj = $.fn.zTree.getZTreeObj("tree");
var selectedNodes = treeObj.getSelectedNodes();
for (var i = 0; i < selectedNodes.length; i++) {
treeObj.removeNode(selectedNodes[i]);
}
}
}
});
layer.close(index);
}, no: function(index) {
layer.close(index);
}
}

});

}

C#

  View Code

public OperationResult Delete(DirectoryInfo directory, OperationResult operation)
{
//從深層子目錄開始遍歷刪除
DirectoryInfo[] childFolder = directory.GetDirectories();

if (operation.Status)
{
//有元素就遍歷刪除,沒有則直接刪除文件夾
if (childFolder.Any())
{
foreach (var directoryInfo in childFolder)
{
Delete(directoryInfo, operation);
FileInfo[] files = directoryInfo.GetFiles();
try
{
foreach (var fileInfo in files)
{
fileInfo.Delete();
}
directoryInfo.Delete();
}
catch (UnauthorizedAccessException ex)
{
operation.Status = false;
operation.Message = string.Format("{0}此文件夾沒有權限訪問!無法執行刪除操作", directoryInfo.FullName);
return operation;
}
}
}
else
{
//驗證文件是否能刪除
try
{
directory.Delete();
}
catch (UnauthorizedAccessException ex)
{
operation.Status = false;
operation.Message = string.Format("{0}此文件夾沒有權限訪問!無法執行刪除操作", directory.FullName);
return operation;
}
}
}
return operation;
}

 

 

3.4  右鍵方法 

思路:點擊右鍵時觸發右鍵事件,在事件方法中把事先寫好的Html菜單展示出來並綁定相應的js事件,根結點沒有刪除與重命名操作,只能新增子節點

javaScript

  View Code

//右鍵方法
function zTreeOnRightClick(event, treeId, treeNode) {
if (treeId == undefined) {
return false;
}

$("#rMenu").css({
top: event.clientY + "px",
left:event.clientX +"px",
display: "block",
"z-index": 1
});

if (treeNode.topNode) {
showItem(["#addFolder"]);
} else {
showItem(["#addFolder", "#reName", "#deleteFile"]);
}

}
//顯示右鍵菜單
function showItem(itemArray) {

for (var i = 0; i < itemArray.length; i++) {

$(itemArray[i]).show();
}
$("#rMenu").hover(function() {
$("#addFolder").click(function() {
//alert("第一次添加!");
zTreeAddNode();
$("#rMenu").hide();
});
$("#reName").click(function() {
Rename();
$("#rMenu").hide();
});
$("#deleteFile").click(function() {
zTreeRemove();
$("#rMenu").hide();
});
},
function() {
for (var j = 0; j < itemArray.length; j++) {
$(itemArray[j]).hide();
}
});

}

3.5 重命名

思路:觸發重命名方法后會使節點處於編輯狀態,失去焦點后自動保存,在保存時先要做驗證沒有相同的文件名,並刷新節點的屬性

注意:因為要先驗證文件名是否已存在,所以先要異步去檢查,但是檢查與執行重命名的方法都是異步的沒法分先后,且方法都要用的zTree提供的方法,所以每次重命名后要重新加載一次整棵樹 體驗有點不太好

javascript

  View Code

function zTreeBeforeRename(treeId, treeNode, newName, isCancel) {
//文件名長度不能超過260個字符(包括路徑)
var zTreeObj = $.fn.zTree.getZTreeObj("tree");
if ((treeNode.fullName + newName).length > 260) {
layer.alert("啊喂~ 你文件名也太長了點吧!", 8);
zTreeObj.editName(treeNode);
}
var status = false;
var path = treeNode.pFullName + "\\" + newName;
//判斷新文件名是否已存在
$.ajax({
url: "/FileManager/CheckRename",
async:false,
type: "POST",
data: {
"path": path,
"isParent": treeNode.isParent
},
dataType: "json",
success: function (data) {
status = data.Status;
if (!data.Status) {
layer.alert(data.Message, 8);
//阻止因Alter反復調用zTreeBeforeRename方法
//zTreeObj.editName(treeNode);
//return false;
} else {
//return true;
}
}
});

if (status) {
return true;
} else {
return false;
}

}

function Rename() {
var zTreeObj = $.fn.zTree.getZTreeObj("tree");
var nodes = zTreeObj.getSelectedNodes();//取到為選中節點數組
zTreeObj.editName(nodes[0]);//把第一個節點變為編輯狀態

}

//重命名
function zTreeOnRename(event, treeId, treeNode, isCancel) {

//把檢查文件名放在此方法中

var zTreeObj = $.fn.zTree.getZTreeObj("tree");

var path = treeNode.fullName;
var destPath = treeNode.pFullName + "\\" + treeNode.name;
var isParent = treeNode.isParent;
//重命名后,fullname,name 都要修改
$.ajax({
url: "/FileManager/RenameFiles",
async:false,
type: "POST",
data: {
"path": path,
"destPath": destPath,
"isParent": isParent
},
dataType: "json",
success: function(data) {
if (data.Status) {

ReLoadTree();

//重命名后刷新父節點,更新子節點中的fullName等屬性
//var selectNodes = zTreeObj.getSelectedNodes();
//var parent = selectNodes[0].getParentNode();
//zTreeObj.reAsyncChildNodes(parent, "refresh");
}
}
});


}

C# 

  View Code

//檢查文件名是否存在
public void CheckFileName(string path, bool isParent,OperationResult operation)
{
if (isParent)
{
if (Directory.Exists(path))
{
operation.Status = false;
operation.Message = "文件夾已經存在了哦!";
}
}
else
{
if (File.Exists(path))
{
operation.Status = false;
operation.Message = "文件已經存在了哦!";
}
}
}
//重命名
public void RenameFileName(string path, string destPath, bool isParent, OperationResult operation)
{
if (isParent)
{
DirectoryInfo directory = new DirectoryInfo(path);
directory.MoveTo(destPath);
}
else
{
FileInfo file = new FileInfo(path);
file.MoveTo(destPath);
}
}

3.6 拖拽方法(移動文件夾,按Ctrl可多選)

思路:根節點不能被拖動,也不能拖為根節點,然后遍歷選中的節點,並傳到Action中反序列化成對象執行移動,並在移動后在前端把節點的屬性pName fullName pFullName更新,避免重新加載樹

javascript

  View Code

//拖拽前的方法
function zTreeBeforeDrag(treeId, treeNodes) {
//根結點不能被移動,
for (var i = 0; i < treeNodes.length; i++) {
if (treeNodes[i].topNode) {
layer.alert("根結點不能被托動哦~", 8);
return false;
}
}
}
//捕獲節點拖拽操作結束之前的事件回調函數,並且根據返回值確定是否允許此拖拽操作
//如果返回 false,zTree 將恢復被拖拽的節點,也無法觸發 onDrop 事件回調函數

function zTreeBeforeDrop(treeId, treeNodes, targetNode, moveType) {
//不能拖拽為根節點
if ((targetNode == null || (moveType != "inner"))) {
return false;
}
if (!targetNode.isParent) {
layer.alert("只能托動到文件夾內哦~", 8);
return false;
}

$.ajax({
url: "FileManager/MoveFiles",
async: false,
type: "POST",
data: {
"nodes": JSON.stringify(treeNodes),
"targetNode": JSON.stringify(targetNode)
},
dataType: "json",
success: function (data) {
if (!data.Status) {
layer.alert(data.Message, 8);
return false;
} else {
//節點拖動后要把的路徑改為新路徑
for (var i = 0; i < treeNodes.length; i++) {
treeNodes[i].fullName = targetNode.fullName + "\\" + treeNodes[i].name;
treeNodes[i].pName = targetNode.name;
treeNodes[i].pFullName = targetNode.fullName;
}
}
}
});
}

C# 

  View Code

public void MoveFilesToFolder(List<zTreeModel> nodes, zTreeModel targetNode,OperationResult operation)
{

foreach (var node in nodes)
{
//文件夾
if (node.isParent)
{
DirectoryInfo directory = new DirectoryInfo(node.fullName);
if (Directory.Exists(targetNode.fullName))
{
//要移動的新地址
string newPath = targetNode.fullName + "\\" + node.name;
directory.MoveTo(newPath);
}
else
{
operation.Status = false;
operation.Message = string.Format("{0}文件夾不存在啊~!", node.fullName);
}

}//文件
else
{
FileInfo file = new FileInfo(node.fullName);
if (Directory.Exists(targetNode.fullName))
{
string newPath = targetNode.fullName + "\\" + node.name;
file.MoveTo(newPath);
}
else
{
operation.Status = false;
operation.Message = string.Format("{0}文件夾不存在啊~!", node.fullName);
}
}

}

}

 

 

四 總結

    雖然這個小工具功能並不強大,但至少是我從有想法到有行動到有成果的一個過程,並且我也享受這一過程,

在開發的過程中,了解了zTree的各種用法,對文件夾,文件的操作也有了更深入的了解,

如果您覺得這個工具有那么點意思 不妨點下推薦哦~您的推薦是我創作源源不斷的動力

 源碼在這里

 

 
 
分類:  開發工具


免責聲明!

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



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