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 /// /// /// namespace WebApi { /// /// /// [RoutePrefix("api/file")] public class IFileController : ApiController { // 基础路径配置 private readonly string _basePath = @"D:\CameraImg"; /// /// 请求DTO /// public class FileRequestDto { public string RelativePath { get; set; } = ""; } /// /// 浏览文件或文件夹,前端传相对路径,返回文件夹内容或文件流(图片等可预览) /// [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}" }); } } /// /// 获取文件预览数据(图片/文本base64,前端可直接预览) /// 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}" }); } } /// /// 判断文件是否可预览 /// 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); } /// /// 安全检查:确保路径在基础目录内,防止路径遍历攻击 /// 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; } } /// /// 根据文件扩展名获取MIME类型(兼容C# 7.3) /// 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(); 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}" }); } } } }