533 lines
21 KiB
C#
533 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Threading.Tasks;
|
|
using IRaCIS.Application.Interfaces;
|
|
using IRaCIS.Application.Contracts;
|
|
using IRaCIS.Core.API.Utility;
|
|
using IRaCIS.Core.Application.Contracts.Image;
|
|
using IRaCIS.Core.Infrastructure.Extention;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
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;
|
|
using IRaCIS.Core.Application.Filter;
|
|
using IRaCIS.Core.Application.Contracts;
|
|
|
|
namespace IRaCIS.Api.Controllers
|
|
{
|
|
/// <summary>
|
|
/// 文件上传
|
|
/// </summary>
|
|
[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<FileController> _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, Microsoft.Extensions.Configuration.IConfiguration config,
|
|
IHostEnvironment hostEnvironment, ILogger<FileController> logger,
|
|
IWebHostEnvironment webHostEnvironment)
|
|
{
|
|
_fileService = fileService;
|
|
_hostEnvironment = hostEnvironment;
|
|
_webHostEnvironment = webHostEnvironment;
|
|
_fileSizeLimit = config.GetValue<long>("FileSizeLimit");
|
|
defaultUploadFilePath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).FullName;
|
|
|
|
_logger = logger;
|
|
|
|
_logger.LogWarning("File Path:" + defaultUploadFilePath);
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 上传文件[FileUpload]
|
|
/// </summary>
|
|
/// <param name="attachmentType">附件类型</param>
|
|
/// <param name="doctorId">医生Id</param>
|
|
/// <returns>返回文件信息</returns>
|
|
[HttpPost, Route("uploadFile/{attachmentType}/{doctorId}")]
|
|
[DisableFormValueModelBinding]
|
|
public async Task<IActionResult> UploadOrdinaryFile(string attachmentType, Guid doctorId)
|
|
{
|
|
#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, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
// 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(FileController), null);
|
|
|
|
#endregion
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 上传文件( 不是医生个人的文件)[FileUpload]
|
|
/// 例如:阅片章程等
|
|
/// </summary>
|
|
/// <param name="type">文件类型</param>
|
|
/// <returns></returns>
|
|
|
|
[HttpPost, Route("uploadNonDoctorFile/{type}")]
|
|
[DisableFormValueModelBinding]
|
|
public async Task<IActionResult> 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, FullFilePath = attachmentPath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) }));
|
|
}
|
|
|
|
section = await reader.ReadNextSectionAsync();
|
|
}
|
|
return Created(nameof(FileController), null);
|
|
|
|
|
|
}
|
|
return new JsonResult(ResponseOutput.NotOk("服务器端映射路径操作失败"));
|
|
#endregion
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// 写文件导到磁盘
|
|
/// </summary>
|
|
/// <param name="stream">流</param>
|
|
/// <param name="path">文件保存路径</param>
|
|
/// <returns></returns>
|
|
public static async Task<int> 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 上传无影响部分
|
|
|
|
/// <summary>
|
|
/// 下载多个医生的所有附件
|
|
/// </summary>
|
|
/// <param name="doctorIds"></param>
|
|
/// <returns></returns>
|
|
|
|
[HttpPost, Route("downloadDoctorAttachments")]
|
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
|
|
{
|
|
|
|
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
|
|
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
{
|
|
FilePath = path,
|
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 下载医生官方简历
|
|
/// </summary>
|
|
/// <param name="language"></param>
|
|
/// <param name="doctorIds"></param>
|
|
/// <returns></returns>
|
|
[HttpPost, Route("downloadOfficialCV/{language}")]
|
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
|
|
{
|
|
|
|
var path = _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
{
|
|
FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
|
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 下载指定医生的指定附件
|
|
/// </summary>
|
|
/// <param name="doctorId">医生Id</param>
|
|
/// <param name="attachmentIds">要下载的附件Id</param>
|
|
/// <returns></returns>
|
|
[HttpPost, Route("downloadByAttachmentId/{doctorId}")]
|
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
|
|
{
|
|
var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
|
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
|
{
|
|
FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
|
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region 废弃
|
|
|
|
public class UploadDTFCommand
|
|
{
|
|
public Guid TrialId { get; set; }
|
|
public Guid SiteId { get; set; }
|
|
public Guid SubjectId { get; set; }
|
|
public Guid SubjectVisitId { get; set; }
|
|
|
|
}
|
|
/// <summary>
|
|
/// 流式上传 临时文件
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpPost("UploadDTF/{trialId:guid}/{siteId:guid}/{subjectId:guid}/{subjectVisitId:guid}/{studyId:guid}")]
|
|
[DisableFormValueModelBinding]
|
|
[DisableRequestSizeLimit]
|
|
[Obsolete]
|
|
public async Task<IResponseOutput> UploadingStream(Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId, Guid studyId, [FromServices] IStudyDTFService studyDTFService)
|
|
{
|
|
|
|
//上传根路径
|
|
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");
|
|
|
|
|
|
var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), trialId.ToString(),
|
|
siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString());
|
|
|
|
if (!Directory.Exists(dtfPath))
|
|
{
|
|
Directory.CreateDirectory(dtfPath);
|
|
}
|
|
|
|
|
|
#region 之前DTF 先上传临时文件,再拷贝
|
|
////上传根路径
|
|
//string uploadFolderPath = Path.Combine(defaultUploadFilePath, "UploadFile");
|
|
////文件类型路径处理
|
|
//var uploadTempFilePath = Path.Combine(uploadFolderPath, "TempFile");
|
|
//if (!Directory.Exists(uploadTempFilePath)) Directory.CreateDirectory(uploadTempFilePath);
|
|
|
|
|
|
//var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
|
|
|
//await WriteFileAsync(section.Body, Path.Combine(uploadTempFilePath, trustedFileNameForFileStorage));
|
|
|
|
//var attachmentPath = $"{uploadTempFilePath}/{trustedFileNameForFileStorage}";
|
|
|
|
////多个文件上传,在这里返回就不合适,需要在外层,返回所有的文件路径,实际需要时再更改
|
|
//return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = attachmentPath });
|
|
|
|
#endregion
|
|
|
|
|
|
//获取boundary
|
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
|
//得到reader
|
|
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
|
|
|
var section = await reader.ReadNextSectionAsync();
|
|
|
|
//读取section
|
|
while (section != null)
|
|
{
|
|
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
|
if (hasContentDispositionHeader)
|
|
{
|
|
var realName = contentDisposition.FileName.Value;
|
|
|
|
var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
|
|
var trustedFileNameForFileStorage = Guid.NewGuid().ToString() + fileNameEX;
|
|
|
|
|
|
var relativePath = $"/Dicom/{DateTime.Now.Year.ToString()}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{trustedFileNameForFileStorage}";
|
|
studyDTFService.AddStudyDTF(new Core.Application.Contracts.Dicom.DTO.StudyDTFAddOrUpdateCommand()
|
|
{
|
|
StudyId = studyId,
|
|
|
|
FileName = realName,
|
|
|
|
Path = relativePath
|
|
});
|
|
|
|
await WriteFileAsync(section.Body, Path.Combine(dtfPath, trustedFileNameForFileStorage));
|
|
|
|
//仅仅返回一个文件,如果多文件上传 在最后返回多个路径
|
|
return ResponseOutput.Ok(new UploadFileInfoDTO() { FilePath = relativePath, FullFilePath = relativePath + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7) });
|
|
}
|
|
section = await reader.ReadNextSectionAsync();
|
|
}
|
|
return ResponseOutput.NotOk("Upload error");
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 流式上传 非Dicom文件
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
[HttpPost("UploadNoneDICOM")]
|
|
[DisableFormValueModelBinding]
|
|
[DisableRequestSizeLimit]
|
|
[Obsolete]
|
|
public async Task<IResponseOutput> UploadingNoneDicomStream([FromForm] ArchiveStudyCommand archiveStudyCommand, [FromServices] IStudyService studyService)
|
|
{
|
|
//上传根路径
|
|
string uploadFolderPath = Path.Combine(defaultUploadFilePath, "Dicom");
|
|
|
|
//文件类型路径处理
|
|
//var noneDicomPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
|
|
// archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString(), "Data");
|
|
|
|
//var dtfPath = Path.Combine(uploadFolderPath, DateTime.Now.Year.ToString(), archiveStudyCommand.TrialId.ToString(),
|
|
// archiveStudyCommand.SiteId.ToString(), archiveStudyCommand.SubjectId.ToString(), archiveStudyCommand.SubjectVisitId.ToString());
|
|
|
|
var noneDicomPath = string.Empty;
|
|
|
|
var dtfPath = string.Empty;
|
|
|
|
var tempDtfPath = Path.Combine(defaultUploadFilePath/*, archiveStudyCommand.DTFPath*/);
|
|
|
|
|
|
if (!Directory.Exists(noneDicomPath))
|
|
{
|
|
Directory.CreateDirectory(noneDicomPath);
|
|
}
|
|
if (!System.IO.File.Exists(tempDtfPath))
|
|
{
|
|
return ResponseOutput.NotOk("DTF file can not found");
|
|
}
|
|
|
|
System.IO.File.Move(tempDtfPath, dtfPath);
|
|
|
|
var saveFileDic = new Dictionary<string, string>();
|
|
|
|
//获取boundary
|
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
|
//得到reader
|
|
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
|
|
|
|
var section = await reader.ReadNextSectionAsync();
|
|
|
|
//读取section
|
|
while (section != null)
|
|
{
|
|
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
|
|
if (hasContentDispositionHeader)
|
|
{
|
|
var fileName = contentDisposition.FileName.Value;
|
|
var fileNameEX = Path.GetExtension(contentDisposition.FileName.Value);
|
|
var trustedFileNameForFileStorage = Guid.NewGuid() + fileNameEX;
|
|
|
|
|
|
|
|
await WriteFileAsync(section.Body, Path.Combine(noneDicomPath, trustedFileNameForFileStorage));
|
|
|
|
var attachmentPath = $"{noneDicomPath}/{trustedFileNameForFileStorage}";
|
|
|
|
saveFileDic.Add(attachmentPath, fileName);
|
|
|
|
|
|
}
|
|
section = await reader.ReadNextSectionAsync();
|
|
}
|
|
|
|
//处理数据库操作
|
|
|
|
//studyService.DealNonDicomFile(saveFileDic, archiveStudyCommand);
|
|
return ResponseOutput.Ok(saveFileDic);
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
}
|