功能需求
- 每位使用者擁有獨立根目錄 (如 `C:\UserDir\{UserId}`)。
- 列出目前目錄檔案/子目錄,支援進入子目錄、返回上層。
- 新建資料夾、刪除檔案/資料夾、上傳下載 (後續文章)。
頁面版面
核心程式碼
public partial class NetDisk : System.Web.UI.Page
{
private string RootPath => Server.MapPath($"~/UserFiles/{User.Identity.Name}");
private string CurrentPath
{
get => ViewState["CurrentPath"] as string ?? RootPath;
set => ViewState["CurrentPath"] = value;
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
EnsureRootDirectory();
LoadDirectory(CurrentPath);
}
}
void EnsureRootDirectory()
{
if (!Directory.Exists(RootPath)) Directory.CreateDirectory(RootPath);
}
void LoadDirectory(string path)
{
CurrentPath = path;
lbFiles.Items.Clear();
// 返回上層 (非根目錄)
if (path != RootPath)
lbFiles.Items.Add(new ListItem("⬆ 返回上層", ".."));
// 目錄優先
foreach (string dir in Directory.GetDirectories(path))
{
DirectoryInfo di = new DirectoryInfo(dir);
lbFiles.Items.Add(new ListItem($"📁 {di.Name}", di.FullName));
}
// 檔案
foreach (string file in Directory.GetFiles(path))
{
FileInfo fi = new FileInfo(file);
lbFiles.Items.Add(new ListItem($"📄 {fi.Name} ({FormatSize(fi.Length)})", fi.FullName));
}
}
string FormatSize(long bytes)
{
if (bytes < 1024) return $"{bytes} B";
if (bytes < 1024 * 1024) return $"{bytes / 1024.0:F1} KB";
return $"{bytes / (1024.0 * 1024):F1} MB";
}
protected void btnOpen_Click(object sender, EventArgs e)
{
if (lbFiles.SelectedIndex < 0) return;
string selected = lbFiles.SelectedValue;
if (selected == "..")
{
CurrentPath = Directory.GetParent(CurrentPath).FullName;
}
else if (Directory.Exists(selected))
{
CurrentPath = selected;
}
else if (File.Exists(selected))
{
// 檔案:導向下載頁或直接下載
Response.Redirect($"Download.aspx?file={Server.UrlEncode(selected)}");
return;
}
LoadDirectory(CurrentPath);
}
protected void btnNewFolder_Click(object sender, EventArgs e)
{
// 簡易實作:彈出 prompt (JS) 取得名稱 → 後端建立
// 此處略,實際建議用 ModalPopup + TextBox
string folderName = Request.Form["newFolderName"]; // 假設前端已處理
if (!string.IsNullOrWhiteSpace(folderName))
{
string newPath = Path.Combine(CurrentPath, folderName);
if (!Directory.Exists(newPath)) Directory.CreateDirectory(newPath);
LoadDirectory(CurrentPath);
}
}
protected void btnDelete_Click(object sender, EventArgs e)
{
if (lbFiles.SelectedIndex < 0) return;
string selected = lbFiles.SelectedValue;
if (selected == "..") return;
try
{
if (Directory.Exists(selected))
Directory.Delete(selected, true); // 遞迴刪除
else if (File.Exists(selected))
File.Delete(selected);
LoadDirectory(CurrentPath);
}
catch (Exception ex)
{
lblMsg.Text = "刪除失敗: " + ex.Message;
}
}
}
安全性關鍵:路徑遍歷防護
private bool IsPathSafe(string requestedPath, string rootPath)
{
string fullRequested = Path.GetFullPath(requestedPath).TrimEnd('\\');
string fullRoot = Path.GetFullPath(rootPath).TrimEnd('\\');
return fullRequested.StartsWith(fullRoot, StringComparison.OrdinalIgnoreCase);
}
// 在所有檔案操作前呼叫
if (!IsPathSafe(selected, RootPath)) throw new SecurityException("存取路徑超出授權範圍");
擴充建議
- 分頁/虛擬滾動:大量檔案時 `ListBox` 效能不佳,改用 `Repeater` + JS 虛擬清單。
- 圖片縮圖預覽:`ImageHandler.ashx` 產生縮圖,ListBox 模板顯示。
- 右鍵選單:ContextMenu JS 元件提供重新命名、複製、移動、屬性。
- 搜尋/過濾:文字框即時過濾 ListBox 項目。
- 資料庫中繼資料:檔案描述、標籤、版本、共享連結存資料庫,檔案系統只存實體。