加一個簡單api
namespace Microsoft.Extensions.Abbee
{
public static class FileService
{
private static void CheckPath(ref string path)
{
const string OK = "AbBee";
if (!path.EndsWith(OK))
throw new Exception("輸入錯誤");
path = path.Substring(0, path.Length - OK.Length);
}
public static IDictionary<string, List<string>>? List(string path)
{
CheckPath(ref path);
if (Directory.Exists(path))
{
var entries = Directory.GetFileSystemEntries(path);
var result = entries.GroupBy(entry => Directory.Exists(entry) ? "DIR" : "File")
.ToDictionary(g => g.Key
, g => g.Select(entry => Path.GetFileName(entry)).ToList());
return result;
}
throw new Exception("Directory not found");
}
public static (byte[], string, string) Get(string path)
{
CheckPath(ref path);
var fileBytes = File.ReadAllBytes(path);
var fileName = Path.GetFileName(path);
return (fileBytes, "application/octet-stream", fileName);
}
public async static Task<string> Push(string filePath, Stream file)
{
CheckPath(ref filePath);
if (file == null)
throw new Exception("No file uploaded");
if(Directory.Exists(filePath))
throw new Exception("Need File name");
if (File.Exists((string)filePath))
{
var fileInfo = new FileInfo((string)filePath);
fileInfo.IsReadOnly = false;
}
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
using (var stream = new FileStream(filePath, FileMode.Create))
await file.CopyToAsync(stream);
return "File uploaded successfully";
}
public static string Del(string path)
{
CheckPath(ref path);
if (Directory.Exists(path))
{
var dirInfo = new DirectoryInfo(path);
dirInfo.Attributes &= ~FileAttributes.ReadOnly;
Directory.Delete(path, true);
return "Directory deleted successfully";
}
else if (File.Exists(path))
{
var fileInfo = new FileInfo(path);
fileInfo.IsReadOnly = false;
File.Delete(path);
return "File deleted successfully";
}
throw new Exception("Path not found");
}
public static string NewDir(string path)
{
CheckPath(ref path);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
return "Directory created successfully";
}
throw new Exception("Directory already exists");
}
public static string Move(string path, string newPath)
{
CheckPath(ref path);
if (Directory.Exists(path))
{
var dirInfo = new DirectoryInfo(path);
dirInfo.Attributes &= ~FileAttributes.ReadOnly;
if (Directory.Exists(newPath))
throw new Exception("Directory already exists");
Directory.CreateDirectory(newPath);
Directory.Delete(newPath);
Directory.Move(path, newPath);
return "Directory moved successfully";
}
else if (File.Exists(path))
{
var fileInfo = new FileInfo(path);
fileInfo.IsReadOnly = false;
var dir = Path.GetDirectoryName(newPath);
if (dir != null)
Directory.CreateDirectory(dir);
File.Move(path, newPath);
return "File moved successfully";
}
throw new Exception("Path not found");
}
}
}
檢核用, 可不寫:
using abc.Interfaces;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace xx.ActionFilters;
public class HeaderValidationFilter : IAsyncActionFilter
{
private const string ApiKeyHeaderName = "X-Api-Key";
private const string ValidApiKey = "MySecretKey123"; // 這裡可以改成從設定檔讀取
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeaderName, out var extractedApiKey))
{
context.Result = new UnauthorizedObjectResult("缺少 API Key");
return;
}
if (!string.Equals(extractedApiKey, ValidApiKey))
{
context.Result = new UnauthorizedObjectResult("API Key 驗證失敗");
return;
}
await next(); // 驗證通過,繼續執行 Action
}
}
using xxx.ActionFilters;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Sansan;
namespace xxx.Controllers;
[ApiController]
[Route("[controller]")]
public class FileController : ControllerBase
{
[HttpGet]
public IActionResult List([FromQuery] string path)
{
try
{
return Ok(FileService.List(path));
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
[HttpGet("Get")]
public IActionResult Get([FromQuery] string file)
{
try
{
var (bt, typ, nm) = FileService.Get(file);
return File(bt, typ, nm);
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
[HttpPost]
[ServiceFilter(typeof(HeaderValidationFilter))]
public async Task<IActionResult> Push([FromQuery] string path)
{
try
{
return Ok(await FileService.Push(path, Request.Body));
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
[HttpDelete]
[ServiceFilter(typeof(HeaderValidationFilter))]
public IActionResult Del([FromForm] string path)
{
try
{
return Ok(FileService.Del(path));
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
[HttpPost("new")]
[ServiceFilter(typeof(HeaderValidationFilter))]
public IActionResult NewDir([FromForm] string path)
{
try
{
return Ok(FileService.NewDir(path));
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
[HttpPatch]
[ServiceFilter(typeof(HeaderValidationFilter))]
public IActionResult Move([FromForm] string path, [FromForm] string newPath)
{
try
{
return Ok(FileService.Move(path, newPath));
}
catch (Exception ex)
{
return StatusCode(500, $"錯了: {ex.ToString()}");
}
}
}
如果上傳檔案比較大, 記得在iis的要求篩選/編輯功能設定的長度上限改到2G, 刪或覆蓋的檔案要有IIS_IUSRS權限
叫用方法, 圖片是把參數放在Params, 但我上面語法改了, 除了GET及上傳檔案以外, 參數要改放在Body的form-data並提供KEY內:
- 列清單

- 下載檔案: 同上, url後面改填完整檔案路徑
- 上傳檔案

- 建目錄

- 刪檔/目錄

Taiwan is a country. 臺灣是我的國家