using System; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text.RegularExpressions; using System.Web.Http; using DataLinkMesWork2; namespace WebApi { [RoutePrefix("api/checkimage")] public class CheckImageController : ApiController { private readonly string _defaultRoot = @"D:\\XDCheckImages"; public class ImageUploadRequest { public string ProductCode { get; set; } public string Remark { get; set; } public string ImageBase64 { get; set; } public string FileExt { get; set; } } public class ImageDownloadRequest { public string Path { get; set; } } private string GetRootPath() { string root = null; try { root = ConfigurationManager.AppSettings["XD_CheckImageRootPath"]; } catch { } if (string.IsNullOrWhiteSpace(root)) { root = _defaultRoot; } return root; } private string SanitizeForPath(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; char[] invalid = Path.GetInvalidFileNameChars(); var value = input.Trim(); foreach (var c in invalid) { value = value.Replace(c.ToString(), string.Empty); } value = value.Replace("/", string.Empty).Replace("\\", string.Empty); return value; } private string GetExtensionFromRequest(ImageUploadRequest request) { string ext = ".jpg"; if (request == null) return ext; if (!string.IsNullOrWhiteSpace(request.FileExt)) { ext = request.FileExt.Trim(); if (!ext.StartsWith(".")) { ext = "." + ext; } return ext; } if (!string.IsNullOrWhiteSpace(request.ImageBase64) && request.ImageBase64.StartsWith("data:", StringComparison.OrdinalIgnoreCase)) { try { var match = Regex.Match(request.ImageBase64, "^data:(?[^;]+);base64,", RegexOptions.IgnoreCase); if (match.Success) { var mime = match.Groups["mime"].Value.ToLowerInvariant(); if (mime == "image/png") return ".png"; if (mime == "image/gif") return ".gif"; if (mime == "image/bmp") return ".bmp"; if (mime == "image/webp") return ".webp"; if (mime == "image/tiff" || mime == "image/tif") return ".tif"; if (mime == "image/jpeg" || mime == "image/jpg") return ".jpg"; } } catch { } } return ext; } 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"; return "application/octet-stream"; } private bool IsPathSafe(string fullPath) { try { var basePath = Path.GetFullPath(GetRootPath()); var requestedPath = Path.GetFullPath(fullPath); return requestedPath.StartsWith(basePath, StringComparison.OrdinalIgnoreCase); } catch { return false; } } [HttpPost] [Route("upload")] public IHttpActionResult Upload([FromBody] ImageUploadRequest request) { try { if (request == null || string.IsNullOrWhiteSpace(request.ProductCode) || string.IsNullOrWhiteSpace(request.Remark) || string.IsNullOrWhiteSpace(request.ImageBase64)) { return Ok(new { success = false, message = "产品编号、备注和图片不能为空" }); } string productCode = SanitizeForPath(request.ProductCode); string remark = SanitizeForPath(request.Remark); if (string.IsNullOrEmpty(productCode)) { return Ok(new { success = false, message = "产品编号无效" }); } string ext = GetExtensionFromRequest(request); string base64 = request.ImageBase64.Trim(); int commaIndex = base64.IndexOf(','); if (base64.StartsWith("data:", StringComparison.OrdinalIgnoreCase) && commaIndex > 0) { base64 = base64.Substring(commaIndex + 1); } byte[] bytes; try { bytes = Convert.FromBase64String(base64); } catch { return Ok(new { success = false, message = "图片数据格式错误" }); } var now = DateTime.Now; string datePart = now.ToString("yyyyMMdd"); string timePart = now.ToString("HHmmss"); string fileName = string.Format("{0}-{1}-{2}{3}", timePart, productCode, remark, ext); string relativePath = string.Format("/{0}/{1}", datePart, fileName); string root = GetRootPath(); string directory = Path.Combine(root, datePart); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } string fullPath = Path.Combine(directory, fileName); if (!IsPathSafe(fullPath)) { return Ok(new { success = false, message = "目标路径不安全" }); } File.WriteAllBytes(fullPath, bytes); string errorMessage; var sqlParameters = new SqlParameter[4]; sqlParameters[0] = new SqlParameter("@产品编号", SqlDbType.NVarChar, 500) { Value = (object)request.ProductCode ?? DBNull.Value }; sqlParameters[1] = new SqlParameter("@备注", SqlDbType.NVarChar, 500) { Value = (object)request.Remark ?? DBNull.Value }; sqlParameters[2] = new SqlParameter("@图片路径", SqlDbType.NVarChar, 500) { Value = (object)relativePath ?? DBNull.Value }; sqlParameters[3] = new SqlParameter("@图片名称", SqlDbType.NVarChar, 500) { Value = (object)fileName ?? DBNull.Value }; bool dbOk = DataAccess2.ExecuteStoredProcedure("XD_检测图片上传_增加", ref sqlParameters, out errorMessage); if (!dbOk) { return Ok(new { success = false, message = "图片已保存,但写入数据库失败:" + errorMessage }); } return Ok(new { success = true, message = "上传成功", data = new { 产品编号 = request.ProductCode, 备注 = request.Remark, 图片路径 = relativePath, 图片名称 = fileName } }); } catch (Exception ex) { return Ok(new { success = false, message = "服务器错误:" + ex.Message }); } } [HttpPost] [Route("download")] public IHttpActionResult Download([FromBody] ImageDownloadRequest request) { try { if (request == null || string.IsNullOrWhiteSpace(request.Path)) { return Ok(new { success = false, message = "文件路径不能为空" }); } string root = GetRootPath(); string relative = request.Path.Replace("\\", "/").Trim(); if (relative.StartsWith("/")) { relative = relative.Substring(1); } string fullPath = Path.Combine(root, relative.Replace('/', Path.DirectorySeparatorChar)); if (!IsPathSafe(fullPath) || !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 MediaTypeHeaderValue(contentType); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("inline") { FileName = fileInfo.Name }; return ResponseMessage(response); } catch (Exception ex) { return Ok(new { success = false, message = "下载文件失败:" + ex.Message }); } } } }