using System; using System.IO; using System.Net; using System.Threading.Tasks; using IRaCIS.Application.Interfaces; using IRaCIS.Application.ViewModels; using IRaCIS.Core.API.Controllers.Common; using IRaCIS.Core.API.Filter; using IRaCIS.Core.API.Utility; using IRaCIS.Core.Application.Contracts.RequestAndResponse; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; namespace IRaCIS.Api.Controllers { /// /// 文件上传 /// [Route("file")] [ApiController, Authorize, ApiExplorerSettings(GroupName = "Common")] public class FileController : ControllerBase { private readonly IFileService _fileService; private readonly IWebHostEnvironment _webHostEnvironment; private readonly IHostEnvironment _hostEnvironment; private ILogger _logger; private string _targetFilePath; private readonly long _fileSizeLimit; private readonly string[] _permittedExtensions = { ".pdf", ".doc", ".docx" }; public string trustedFileNameForFileStorage = ""; // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions _defaultFormOptions = new FormOptions(); private string defaultUploadFilePath = string.Empty; public FileController(IFileService fileService, IConfiguration config, IHostEnvironment hostEnvironment, ILogger logger, IWebHostEnvironment webHostEnvironment) { _fileService = fileService; _hostEnvironment = hostEnvironment; _webHostEnvironment = webHostEnvironment; _fileSizeLimit = config.GetValue("FileSizeLimit"); defaultUploadFilePath = Directory.GetParent(_hostEnvironment.ContentRootPath).FullName; _logger = logger; _logger.LogError("File Path:"+ defaultUploadFilePath); // To save physical files to a path provided by configuration: //_targetFilePath = config.GetValue("StoredFilesPath"); // To save physical files to the temporary files folder, use: //_targetFilePath = Path.GetTempPath(); } /// /// 上传文件[FileUpload] /// /// 附件类型 /// 医生Id /// 返回文件信息 [HttpPost, Route("uploadFile/{attachmentType}/{doctorId}")] [DisableFormValueModelBinding] public async Task UploadOrdinaryFile(string attachmentType, Guid doctorId) { _logger.LogError("aaaa"); #region 官方文档方式 if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { ModelState.AddModelError("File", $"The request couldn't be processed (Error 1)."); // Log error return BadRequest(ModelState); } var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse( section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { // This check assumes that there's a file // present without form data. If form data // is present, this method immediately fails // and returns the model error. if (!MultipartRequestHelper .HasFileContentDisposition(contentDisposition)) { ModelState.AddModelError("File", $"The request couldn't be processed (Error 2)."); // Log error return BadRequest(ModelState); } else { // Don't trust the file name sent by the client. To display // the file name, HTML-encode the value. var trustedFileNameForDisplay = WebUtility.HtmlEncode( contentDisposition.FileName.Value); //var trustedFileNameForFileStorage = Path.GetRandomFileName(); trustedFileNameForFileStorage = contentDisposition.FileName.Value; // **WARNING!** // In the following example, the file is saved without // scanning the file's contents. In most production // scenarios, an anti-virus/anti-malware scanner API // is used on the file before making the file available // for download or for use by other systems. // For more information, see the topic that accompanies // this sample. var streamedFileContent = await FileHelpers.ProcessStreamedFile( section, contentDisposition, ModelState, _permittedExtensions, _fileSizeLimit); if (!ModelState.IsValid) { return BadRequest(ModelState); } //实际文件处理 string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile/" + doctorId + "/"); var doctorAttachmentUploadFolder = Path.Combine(uploadFolderPath, attachmentType); _targetFilePath = doctorAttachmentUploadFolder; if (!Directory.Exists(doctorAttachmentUploadFolder)) Directory.CreateDirectory(doctorAttachmentUploadFolder); using (var targetStream = System.IO.File.Create( Path.Combine(_targetFilePath, trustedFileNameForFileStorage))) { await targetStream.WriteAsync(streamedFileContent); var attachmentPath = $"/UploadFile/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}"; return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath })); } } } // Drain any remaining section body that hasn't been consumed and // read the headers for the next section. section = await reader.ReadNextSectionAsync(); } return Created(nameof(FileTestController), null); #endregion } /// /// 上传文件( 不是医生个人的文件)[FileUpload] /// 例如:阅片章程等 /// /// 文件类型 /// [HttpPost, Route("uploadNonDoctorFile/{type}")] [DisableFormValueModelBinding] public async Task UploadNonDoctorFile(string type) { #region New Test 实测OK //上传根路径 string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile"); if (uploadFolderPath != null) { //文件类型路径处理 var uploadTypePath = Path.Combine(uploadFolderPath, type); if (!Directory.Exists(uploadTypePath)) Directory.CreateDirectory(uploadTypePath); ////实际文件处理 _targetFilePath = uploadTypePath; //获取boundary #region 方式二 var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); #endregion #region 方式一 //var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value; #endregion //得到reader var reader = new MultipartReader(boundary, HttpContext.Request.Body); //{ BodyLengthLimit = 2000 };// var section = await reader.ReadNextSectionAsync(); //读取section while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { trustedFileNameForFileStorage = contentDisposition.FileName.Value; await WriteFileAsync(section.Body, Path.Combine(_targetFilePath, trustedFileNameForFileStorage)); #region 方式二 //using (var targetStream = System.IO.File.Create( // Path.Combine(_targetFilePath, trustedFileNameForFileStorage))) //{ // using (var memoryStream = new MemoryStream()) // { // await section.Body.CopyToAsync(memoryStream); // await targetStream.WriteAsync(memoryStream.ToArray()); // } //} #endregion var attachmentPath = $"/UploadFile/{type}/{trustedFileNameForFileStorage}"; return new JsonResult(ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath })); } section = await reader.ReadNextSectionAsync(); } return Created(nameof(FileController), null); } return new JsonResult(ResponseOutput.NotOk("服务器端映射路径操作失败")); #endregion } /// /// 写文件导到磁盘 /// /// 流 /// 文件保存路径 /// public static async Task WriteFileAsync(System.IO.Stream stream, string path) { const int FILE_WRITE_SIZE = 84975;//写出缓冲区大小 int writeCount = 0; using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Write, FILE_WRITE_SIZE, true)) { byte[] byteArr = new byte[FILE_WRITE_SIZE]; int readCount = 0; while ((readCount = await stream.ReadAsync(byteArr, 0, byteArr.Length)) > 0) { await fileStream.WriteAsync(byteArr, 0, readCount); writeCount += readCount; } } return writeCount; } #region 上传无影响部分 /// /// 下载多个医生的所有附件 /// /// /// [HttpPost, Route("downloadDoctorAttachments")] public IResponseOutput DownloadAttachment(Guid[] doctorIds) { return ResponseOutput.Ok(new UploadFileInfoDTO { FilePath = _fileService.CreateDoctorsAllAttachmentZip(doctorIds) }); } /// /// 下载医生官方简历 /// /// /// [HttpPost, Route("downloadOfficialCV")] public IResponseOutput DownloadOfficialResume(Guid[] doctorIds) { return ResponseOutput.Ok(new UploadFileInfoDTO { FilePath = _fileService.CreateOfficialResumeZip(doctorIds) }); } /// /// 下载指定医生的指定附件 /// /// 医生Id /// 要下载的附件Id /// [HttpPost, Route("downloadByAttachmentId/{doctorId}")] public IResponseOutput DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds) { return ResponseOutput.Ok(new UploadFileInfoDTO { FilePath = _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds) }); } #endregion } }