278 lines
10 KiB
C#
278 lines
10 KiB
C#
using System.Net.Http;
|
||
using System.Net;
|
||
using System.Web.Http;
|
||
using Newtonsoft.Json.Linq;
|
||
using System.Threading.Tasks;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System;
|
||
|
||
/////http://127.0.0.1:9981/api/file/browse
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
namespace WebApi
|
||
{
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
[RoutePrefix("api/file")]
|
||
public class IFileController : ApiController
|
||
{
|
||
// 基础路径配置
|
||
private readonly string _basePath = @"D:\项目文件";
|
||
|
||
/// <summary>
|
||
/// 请求DTO
|
||
/// </summary>
|
||
public class FileRequestDto
|
||
{
|
||
public string RelativePath { get; set; } = "";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 浏览文件或文件夹,前端传相对路径,返回文件夹内容或文件流(图片等可预览)
|
||
/// </summary>
|
||
[HttpPost]
|
||
[Route("browse")]
|
||
public IHttpActionResult BrowsePath([FromBody] FileRequestDto request)
|
||
{
|
||
try
|
||
{
|
||
if (request == null)
|
||
request = new FileRequestDto();
|
||
|
||
if (string.IsNullOrEmpty(request.RelativePath) || request.RelativePath == "/")
|
||
{
|
||
request.RelativePath = "";
|
||
}
|
||
|
||
string fullPath = Path.Combine(_basePath, request.RelativePath.Replace('/', Path.DirectorySeparatorChar));
|
||
|
||
if (!IsPathSafe(fullPath))
|
||
{
|
||
return Ok(new { success = false, message = "无效的路径" });
|
||
}
|
||
|
||
if (!Directory.Exists(fullPath) && !System.IO.File.Exists(fullPath))
|
||
{
|
||
return Ok(new { success = false, message = "路径不存在" });
|
||
}
|
||
|
||
if (System.IO.File.Exists(fullPath))
|
||
{
|
||
return GetFilePreview(fullPath, request.RelativePath);
|
||
}
|
||
|
||
if (Directory.Exists(fullPath))
|
||
{
|
||
return GetDirectoryContents(fullPath, request.RelativePath);
|
||
}
|
||
|
||
return Ok(new { success = false, message = "路径不存在" });
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return Ok(new { success = false, message = $"服务器错误: {ex.Message}" });
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取文件预览数据(图片/文本base64,前端可直接预览)
|
||
/// </summary>
|
||
private IHttpActionResult GetFilePreview(string fullPath, string relativePath)
|
||
{
|
||
try
|
||
{
|
||
var fileInfo = new FileInfo(fullPath);
|
||
var extension = fileInfo.Extension.ToLowerInvariant();
|
||
var previewable = IsPreviewableFile(extension);
|
||
var contentType = GetContentType(fileInfo.Name);
|
||
|
||
bool tooLarge = fileInfo.Length > 10 * 1024 * 1024; // 10MB限制
|
||
string base64String = null;
|
||
string dataUrl = null;
|
||
bool canPreview = previewable && !tooLarge;
|
||
if (canPreview)
|
||
{
|
||
var fileBytes = System.IO.File.ReadAllBytes(fullPath);
|
||
base64String = Convert.ToBase64String(fileBytes);
|
||
dataUrl = $"data:{contentType};base64,{base64String}";
|
||
}
|
||
|
||
var result = new
|
||
{
|
||
success = true,
|
||
type = "file",
|
||
path = relativePath,
|
||
name = fileInfo.Name,
|
||
size = fileInfo.Length,
|
||
extension = fileInfo.Extension,
|
||
contentType = contentType,
|
||
isPreviewable = canPreview,
|
||
base64Data = base64String,
|
||
dataUrl = dataUrl,
|
||
created = fileInfo.CreationTime,
|
||
modified = fileInfo.LastWriteTime,
|
||
message = tooLarge ? "文件过大,无法预览,可下载" : null
|
||
};
|
||
return Ok(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return Ok(new { success = false, message = $"获取文件预览失败: {ex.Message}" });
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 判断文件是否可预览
|
||
/// </summary>
|
||
private bool IsPreviewableFile(string extension)
|
||
{
|
||
var previewableExtensions = new[]
|
||
{
|
||
".bmp", ".jpg", ".jpeg", ".png", ".gif", ".tiff", ".tif", ".webp",
|
||
".txt", ".json", ".xml", ".csv", ".log", ".md"
|
||
};
|
||
return previewableExtensions.Contains(extension);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 安全检查:确保路径在基础目录内,防止路径遍历攻击
|
||
/// </summary>
|
||
private bool IsPathSafe(string fullPath)
|
||
{
|
||
try
|
||
{
|
||
var basePath = Path.GetFullPath(_basePath);
|
||
var requestedPath = Path.GetFullPath(fullPath);
|
||
return requestedPath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase);
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据文件扩展名获取MIME类型(兼容C# 7.3)
|
||
/// </summary>
|
||
private string GetContentType(string fileName)
|
||
{
|
||
var extension = Path.GetExtension(fileName).ToLowerInvariant();
|
||
if (extension == ".bmp") return "image/bmp";
|
||
if (extension == ".jpg" || extension == ".jpeg") return "image/jpeg";
|
||
if (extension == ".png") return "image/png";
|
||
if (extension == ".gif") return "image/gif";
|
||
if (extension == ".tiff" || extension == ".tif") return "image/tiff";
|
||
if (extension == ".webp") return "image/webp";
|
||
if (extension == ".pdf") return "application/pdf";
|
||
if (extension == ".txt") return "text/plain";
|
||
if (extension == ".json") return "application/json";
|
||
if (extension == ".xml") return "application/xml";
|
||
if (extension == ".csv") return "text/csv";
|
||
if (extension == ".md") return "text/markdown";
|
||
if (extension == ".log") return "text/plain";
|
||
if (extension == ".zip") return "application/zip";
|
||
if (extension == ".rar") return "application/x-rar-compressed";
|
||
return "application/octet-stream";
|
||
}
|
||
|
||
private IHttpActionResult GetDirectoryContents(string fullPath, string relativePath)
|
||
{
|
||
try
|
||
{
|
||
var items = new List<object>();
|
||
var directories = Directory.GetDirectories(fullPath);
|
||
foreach (var dir in directories)
|
||
{
|
||
var dirInfo = new DirectoryInfo(dir);
|
||
var subPath = string.IsNullOrEmpty(relativePath)
|
||
? dirInfo.Name
|
||
: $"{relativePath}/{dirInfo.Name}";
|
||
items.Add(new
|
||
{
|
||
name = dirInfo.Name,
|
||
type = "directory",
|
||
path = subPath,
|
||
created = dirInfo.CreationTime,
|
||
modified = dirInfo.LastWriteTime,
|
||
isPreviewable = false
|
||
});
|
||
}
|
||
var files = Directory.GetFiles(fullPath);
|
||
foreach (var file in files)
|
||
{
|
||
var fileInfo = new FileInfo(file);
|
||
var filePath = string.IsNullOrEmpty(relativePath)
|
||
? fileInfo.Name
|
||
: $"{relativePath}/{fileInfo.Name}";
|
||
var extension = fileInfo.Extension.ToLowerInvariant();
|
||
items.Add(new
|
||
{
|
||
name = fileInfo.Name,
|
||
type = "file",
|
||
path = filePath,
|
||
size = fileInfo.Length,
|
||
extension = fileInfo.Extension,
|
||
created = fileInfo.CreationTime,
|
||
modified = fileInfo.LastWriteTime,
|
||
isPreviewable = IsPreviewableFile(extension),
|
||
contentType = GetContentType(fileInfo.Name)
|
||
});
|
||
}
|
||
var result = new
|
||
{
|
||
success = true,
|
||
type = "directory",
|
||
path = relativePath,
|
||
items = items.OrderBy(x => ((dynamic)x).type == "directory" ? 0 : 1)
|
||
.ThenBy(x => ((dynamic)x).name)
|
||
.ToList()
|
||
};
|
||
return Ok(result);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return Ok(new { success = false, message = $"读取目录失败: {ex.Message}" });
|
||
}
|
||
}
|
||
|
||
[HttpPost]
|
||
[Route("download")]
|
||
public IHttpActionResult DownloadFile([FromBody] FileRequestDto request)
|
||
{
|
||
try
|
||
{
|
||
if (request == null || string.IsNullOrEmpty(request.RelativePath))
|
||
return Ok(new { success = false, message = "文件路径不能为空" });
|
||
|
||
string fullPath = Path.Combine(_basePath, request.RelativePath.Replace('/', Path.DirectorySeparatorChar));
|
||
if (!IsPathSafe(fullPath) || !System.IO.File.Exists(fullPath))
|
||
return Ok(new { success = false, message = "文件不存在" });
|
||
|
||
var fileInfo = new FileInfo(fullPath);
|
||
var contentType = GetContentType(fileInfo.Name);
|
||
|
||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||
{
|
||
Content = new StreamContent(new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||
};
|
||
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
|
||
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
|
||
{
|
||
FileName = fileInfo.Name
|
||
};
|
||
return ResponseMessage(response);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return Ok(new { success = false, message = $"下载文件失败: {ex.Message}" });
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|