ExtJS 实现的Web文件管理系统
上次的文章发了就等到了一个面试机会,还没成就来北京了上了一个.net培训班。这个是这里老师安排的作业,让用.netAJAX实现的,想了想觉得那ExtJS+XML写可能更好这个东西就出来,主要思想是用ExtJS实现前台的UI,然后用AJAX方式实现和服务器的信息交互,这里没有用到一下ASP.NET下的AJAX实现,就是用的ExtJS自己的AJAX实现。主要是因为效率的考虑,再有就是ExtJS构建的页面基本上觉得就没有使用其他AJAX技术的必要了,他的页面已经没有刷新了,要的就是数据而已,数据是通过一般处理程序生产XML实现的。下面就是具体的实现方式,和遇到的一些问题。
ExtJS我也是第一次使用,我希望我的经历也可以给和我一样初学ExtJS的朋友一些提示(毕竟当时走了不少弯路)。
ExtJS 和Jquery很像大部分的功能实现都是在初始化的时候实现的。基本没有是用一个input调用的。个人觉得这个比JQuery做的还有很,比如 JQuery的AJAX还可以写在input里面,但是ExtJS你会发现你要是这么写了,其他的东西就很难编写了。Ext还像C#一样实现了:属性,方法,甚至实现了事件的机制。大部分ExtJS控件都有一个handler可以通过配置它来出发实现一些事件。而在定义ExtJS控件的时候有点像定义 ASP.NET控件一样有一些配置的信息跟在其后,看了ExtJS以后举得这个东西可以说是完全的JS的面向对象了,强悍强悍。
下面我就介绍一下我这里web文件管理系统了
首先是一些文件压缩,拷贝等的BLL这里就不做太多的介绍了,就是一些System.File.IO的一些为对文件进行操作的类,已实现前台的一些功能。
用于文件描述的类
IO文件操作类
using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
/// <summary>
///realize file manager handler (new folder,new file,copy,paste,del,move,rename,cut)
/// </summary>
namespace DragonJ
{
public class FileManager
{
public FileManager()
{
//
//TODO: 在此处添加构造函数逻辑
//
}
public string path { get; set; }
public FileManager(string ParentPath)
{
this.path = ParentPath;
}
/// <summary>
/// 复制文件和文件夹(外部访问)
/// </summary>
/// <param name="list"></param>
/// <param name="comedir"></param>
/// <param name="todir"></param>
/// <returns></returns>
public bool CopyFiles(List<DealFiles> list, string comedir, string todir)
{
bool ret = false;
try
{
foreach (DealFiles i in list)
{
if (i.fileType == FileType.Folder)
{
FolderCopy(i.FileName, comedir, todir);
}
else
{
DataCopy(i.FileName, comedir, todir);
}
}
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 复制文件到指定目录
/// </summary>
/// <param name="filename"></param>
/// <param name="comedir"></param>
/// <param name="todir"></param>
/// <returns></returns>
private bool DataCopy(string filename, string comedir, string todir)
{
bool ret = false;
string new_filename;
if (comedir == todir)
{
new_filename = "复制" + filename;
}
else
{
new_filename = filename;
}
try
{
File.Copy(Path.Combine(comedir, filename), Path.Combine(todir, filename));
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 复制文件夹到指定目录
/// </summary>
/// <param name="folderName"></param>
/// <param name="comedir"></param>
/// <param name="todir"></param>
/// <returns></returns>
private bool FolderCopy(string folderName, string comedir, string todir)
{
bool ret = false;
string new_folderName;
if (comedir == todir)
{
new_folderName = "复制" + folderName;
}
else
{
new_folderName = folderName;
}
try
{
if (Directory.Exists(Path.Combine(comedir, folderName)))
{
string[] files = Directory.GetFiles(Path.Combine(comedir, folderName));
Directory.CreateDirectory(Path.Combine(todir, folderName));
foreach (string file in files)
{
DataCopy(Path.GetFileName(file), Path.Combine(comedir, folderName), Path.Combine(todir, folderName));
}
//这里的没有向zip类中写的父目录不断更改,看看是否好使
string[] dirs = Directory.GetDirectories(Path.Combine(comedir, folderName));
foreach (string dir in dirs)
{
if (!FolderCopy(Path.GetFileName(dir), Path.Combine(comedir, folderName), Path.Combine(todir, folderName)))
{
return false;
}
}
ret = true;
}
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 删除文件和文件夹(外部访问)
/// </summary>
/// <param name="list"></param>
/// <param name="dir"></param>
/// <returns></returns>
public bool DelFiles(List<DealFiles> list, string dir)
{
bool ret = false;
try
{
foreach (DealFiles i in list)
{
if (i.fileType == FileType.Folder)
{
FolderDel(Path.Combine(dir, i.FileName));
}
else
{
DataDel(i.FileName, dir);
}
}
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 删除文件
/// </summary>
/// <param name="filename"></param>
/// <param name="dir"></param>
/// <returns></returns>
private bool DataDel(string filename, string dir)
{
bool ret = false;
try
{
File.Delete(Path.Combine(dir, filename));
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 删除文件夹(包括其子目录)
/// </summary>
/// <param name="dir"></param>
/// <returns></returns>
private bool FolderDel(string dir)
{
bool ret = false;
try
{
Directory.Delete(dir, true);
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 移动文件和文件夹
/// </summary>
/// <param name="filename"></param>
/// <param name="comedir"></param>
/// <param name="todir"></param>
/// <returns></returns>
public bool MoveFiles(List<DealFiles> list, string comedir, string todir)
{
bool ret = false;
try
{
CopyFiles(list, comedir, todir);
DelFiles(list, comedir);
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="folderName"></param>
/// <returns></returns>
public bool CreatFolder(string folderName)
{
bool ret = false;
try
{
Directory.CreateDirectory(Path.Combine(path, folderName));
ret = true;
}
catch (Exception e)
{
throw e;
}
return ret;
}
/// <summary>
/// 创建文件
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public bool CreatFile(string fileName, string text)
{
bool ret = false;
System.IO.FileStream fs = null;
try
{
fs = File.Open(Path.Combine(path, fileName), FileMode.CreateNew, FileAccess.ReadWrite);
byte[] butter = System.Text.Encoding.Default.GetBytes(text);
byte[] but = new byte[16];
fs.Write(butter, 0, butter.Length);
ret = true;
}
catch (Exception e)
{
throw e;
}
finally
{
fs.Dispose();
}
return ret;
}
public System.Text.StringBuilder ReadFile(string fileName)
{
System.Text.StringBuilder str = new System.Text.StringBuilder();
System.IO.FileStream fs = null;
try
{
fs = File.Open(Path.Combine(path, fileName), FileMode.Open);
int length = 0;
System.Text.Encoding GB = System.Text.Encoding.GetEncoding(20936);
byte[] but = new byte[16];
while ((length = fs.Read(but, 0, but.Length)) > 0)
{
str.Append(GB.GetString(but));
}
}
catch (Exception e)
{
throw e;
}
finally
{
fs.Dispose();
}
return str;
}
}
文件压缩类
public class ZipClass
{
///
/// 递归压缩文件夹方法
///
///
private static bool ZipFileDictory(string FolderToZip, ZipOutputStream s, string ParentFolderName, string RootFolder)
{
bool res = true;
string[] folders, filenames;
ZipEntry entry = null;
FileStream fs = null;
Crc32 crc = new Crc32();
try
{
//创建当前文件夹
//entry = new ZipEntry(Path.Combine(ParentFolderName, Path.GetFileName(FolderToZip) + "//")); //加上 “/” 才会当成是文件夹创建
//s.PutNextEntry(entry);
//s.Flush();
//先压缩文件,再递归压缩文件夹
filenames = Directory.GetFiles(Path.Combine(ParentFolderName, FolderToZip));
foreach (string file in filenames)
{
//打开压缩文件
fs = File.OpenRead(file);
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
string URL = file.Replace(RootFolder+@"/", "");
entry = new ZipEntry(URL);
entry.DateTime = DateTime.Now;
entry.Size = fs.Length;
fs.Close();
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
s.PutNextEntry(entry);
s.Write(buffer, 0, buffer.Length);
}
}
catch
{
res = false;
}
finally
{
if (fs != null)
{
fs.Close();
fs = null;
}
if (entry != null)
{
entry = null;
}
GC.Collect();
GC.Collect(1);
}
folders = Directory.GetDirectories(Path.Combine(ParentFolderName, FolderToZip));
foreach (string folder in folders)
{
if (!ZipFileDictory(Path.GetFileName(folder), s, Path.Combine(ParentFolderName, FolderToZip),RootFolder))
{
return false;
}
}
return res;
}
///
/// 压缩文件
///
/// 要进行压缩的文件名
/// 压缩后生成的压缩文件名
///
private static bool ZipFile(string FileToZip, ZipOutputStream s, string ParentFolderName, string RootFolder)
{
//如果文件没有找到,则报错
System.IO.DirectoryInfo di = new DirectoryInfo(ParentFolderName);
if (!File.Exists(System.IO.Path.Combine(ParentFolderName, FileToZip)))
{
throw new System.IO.FileNotFoundException("指定要压缩的文件: " + FileToZip + " 不存在!");
}
FileStream fs = null;
ZipEntry ZipEntry = null;
bool res = true;
try
{
//打开压缩文件
fs = File.OpenRead(Path.Combine(ParentFolderName,FileToZip));
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
string URL=Path.Combine(ParentFolderName.Replace(RootFolder,""), FileToZip);
ZipEntry = new ZipEntry(URL);
ZipEntry.DateTime = DateTime.Now;
ZipEntry.Size = fs.Length;
fs.Close();
s.PutNextEntry(ZipEntry);
s.Write(buffer, 0, buffer.Length);
}
catch
{
res = false;
}
finally
{
if (ZipEntry != null)
{
ZipEntry = null;
}
if (fs != null)
{
fs.Close();
fs = null;
}
GC.Collect();
GC.Collect(1);
}
return res;
}
///
/// 压缩文件 和 文件夹
///
/// 待压缩的文件或文件夹,全路径格式
/// 压缩后生成的压缩文件名,全路径格式
///
public static bool Zip(List<DealFiles> FilesList, string ParentFolderName, string ZipFileName)
{
bool ret = true;
System.IO.DirectoryInfo di = new DirectoryInfo(ParentFolderName);
ZipOutputStream s = null;
if (di.Exists)
{
try
{
s = new ZipOutputStream(File.Create(System.IO.Path.Combine(ParentFolderName, ZipFileName + ".zip")));
s.SetLevel(6);
foreach (DealFiles i in FilesList)
{
if (i.fileType == FileType.Folder)
{
ZipFileDictory(i.FileName, s, ParentFolderName, ParentFolderName);
}
else
{
ZipFile(i.FileName, s, ParentFolderName, ParentFolderName);
}
}
}
catch (FileNotFoundException e)
{
ret = false;
throw e;
}
catch (FileLoadException e)
{
ret = false;
throw e;
}
catch (FieldAccessException e)
{
ret = false;
throw e;
}
finally
{
if (s != null)
{
s.Close();
s = null;
}
}
}
else
{
}
return ret;
}
文件解压缩类
看了这些大家一定发现上面操作的类都是用的一个List实现的这是为了,这是为了让ExtJS实现多文件选择而设计的,Ext会用AJAX回传一个选择文件的列表给服务器,然后转化成这里用到的List进行操作。
下面这个类是用来实现ExtJS的文件管理导航的即文件夹的的前进,后退,向上。这个是用的一个的栈实现的,原理我就不说了,说栈大家估计就明白了,本来是想直接用JS实现的,但是有想起来了序列化,正好没有什么地方联系就用了序列化实现的这个功能,这个功能是仿照的ViewState的原理实现的,在每次点击页面的时候会产生一个AJAX回传,把数据给服务器,服务器将数据放在一下这个类中,然后经过序列化成Base64在传给页面的一个隐藏字段,这个字段在回传的时候一起回传在服务器上经过反序列化再生成这个类的一个实例。
using System;
using System.Collections.Generic;
using System.Web;
/// <summary>
///realize web page manager PgUp PgDn Parenet refurbish
/// </summary>
namespace DragonJ
{
[System.Serializable]
public class PageManager
{
private Stack<string> forwardURL = new Stack<string>(5);
private Stack<string> backURL = new Stack<string>(5);
//public string path
//{
// get { return path; }
// set { path = value; }
//}
public string path { get; set; }
public PageManager(string Path)
{
//
//TODO: 在此处添加构造函数逻辑
//
this.path = Path;
}
public void enterNewPage(string urlCome)
{
backURL.Push(path);
this.path = urlCome;
}
public string enterForward()
{
string url=string.Empty;
if (forwardURL.Count != 0)
{
string urlCome = this.path;
url = forwardURL.Pop() as string;
backURL.Push(urlCome);
this.path = url;
}
return url;
}
public string enterBack()
{
string url = string.Empty;
if (backURL.Count != 0)
{
string urlCome = this.path;
url = backURL.Pop() as string;
forwardURL.Push(urlCome);
this.path = url;
}
return url;
}
public string upFolder()
{
string url = string.Empty;
string urlCome = this.path;
url=System.IO.Path.GetDirectoryName(urlCome);
backURL.Push(urlCome);
this.path = url;
return url;
}
}
用于功能的类现在已经写完了,现在就让我们一起来来用ExtJS来实现这些功能,在这之前我们介绍一下ExtJS的基本用法,ExtJS自己写了不少控件,这些控件就是我们要用来搭建我们UI的,这些控件都是要在页面初始化的时候配置的,ExtJS有一个Ext.onReady事件,在这里面加载你要的控件配置他们就可以等到效果了。下面是一个简单的GridPanel配置的例子。
grid = new Ext.grid.GridPanel({
id: "grid_view",
title: "文件管理",
width: 800,
height: 400,
region: "east",
sm: sm,
cm: cm,
renderTo: document.body,
frame: true,
store: store
});
大家可以看到这里的写发用的是JOSN,个人觉得JOSN是个好东西能传数据(拿JOSN传数据要比XML省空间),又能用来实现JS的面向对象。大家看见了ExtJS就是用JOSN实现面向对象的写代码方式,而且配合JS的特点和JOSN你的JS代码就可以实现方法了,因为在JS里面几乎所有的东西都可以看成变量,函数也不例外,这只要在定义一个名值对在值的里面写一个函数就能轻松的实现一个带方法的类了。
在ExtJS里面也是用的这种方式来是实现的,很多控件都有一个handler或是callback属性,有的有listener这些后面都是跟的一个函数,来加载事件。
下面就是我这个系统里面GridPanel的完整配置。
grid = new Ext.grid.GridPanel({
id: "grid_view",
title: "文件管理",
width: 800,
height: 400,
region: "east",
sm: sm,//加载选择模板
cm: cm,//加载模板列
//这个是底部工具条
bbar: [
{ xtype: "field",
width: 200,
handler: function() { }
}, '-', {
text: "前往",
handler: function() {
EnterPage(grid.getBottomToolbar().items.item(0).getValue());
}
}],
//顶部工具条
tbar: [{
text: "后退",
handler: function() { DealPage("back"); }
}, '-', {
text: "前进",
handler: function() { DealPage("forward"); }
}, '-', {
text: "向上",
handler: function() { DealPage("up"); }
}, '-', {
text: "新建文件",
handler: function() { NewFiles("NewFile"); }
}, '-', {
text: "新建文件夹",
handler: function() { NewFiles("NewFolder"); }
}, '-', {
text: "复制",
handler: function() { Reg_Copy_Files(); }
}, '-', {
text: "粘贴",
handler: function() { PasteFiles(); }
}, '-', {
text: "删除",
handler: function() { DelFiles(); }
}, '-', {
text: "移动",
handler: function() { Reg_Move_Files(); }
}, '-', {
text: "取消",
disabled: true,
handler: function() { Reset_RegDatas(); }
}, '-', {
text: "压缩",
handler: function() { ZipFiles(); }
}, '-', {
text: "解压缩",
handler: function() { UnZipFile(); }
}],
viewConfig: {
forceFit: true
},
renderTo: document.body,//指定Grid记载到那个页面元素
frame: true,
store: store//加载数据源
});
grid.on("rowdblclick", rowdbClick);//添加行的双击事件
grid.on("rowcontextmenu", open_rowcontextmenu);//添加行的右键事件
grid.getBottomToolbar().items.item(0).setValue("D://");//初始化底部工具栏的路径信息
});
基本页面上面的每个功能都是通过AJAX回传来实现的,这里没有应用.net的AJAX框架,就是用的ExtJS的AJAX回传技术。下面是一个AJAX回传的例子。
url: "FileManager.ashx",
method: "Post",
params: { Path: SelectDataItems, Type: "Copy", Root: document.getElementById("Now_Path").value, FromRoot: fromURL },
callback: function(options, success, response) {
window.alert(response.responseText);
window.alert(response.responseText == "OK");
if (success && response.responseText == "OK") {
Ext.MessageBox.alert("文件操作提示", "拷贝数据成功");
Rerender(document.getElementById("Now_Path").value);
}
else {
Ext.MessageBox.alert("文件操作提示", "拷贝数据出错");
Rerender(document.getElementById("Now_Path").value);
}
grid.getTopToolbar().items.item(10).enable();
grid.getTopToolbar().items.item(18).enable();
}
});
- url:要回传到的网址
- method:回传的方式有GET,POST
- params:回传的时候带了参数(以JOSN方式数据格式输入)
- callback:AJAX回传后出触发事件
在callback事件中的函数有三个参数分别是
- options:用来回传的参数
- success:回传是否成功
-
response:这个就是关键了这是一个XMLHttpRequest类型的回传值,也就是说这个就是AJAX的回传结构。
不想把一个文章写的太长这样不好看,呵呵剩下具体功能还是写到会面的文章里了,这里说一下自己对ExtJS的编写代码的理解,因为ExtJS的文档不是很全,而且官方的文档是E文的所以刚刚开始的时候没有爱看,但是渐渐发现其实这个是最有用的,虽然上面没有多少例子,那个还是非常有用的,还有就是不要看中文的那个API文档用了以后才知道那个上面的东西不全!!!建议大家开始的时候看看别人的一些例子,然后就自己写个好玩的东东,不会了就去看API文档,文档的里面每项都有值的类型,如果那个项看不懂就去看看它的类型的文档。
最后给出整个的ExtJS的界面源码