339 lines
13 KiB
C#
339 lines
13 KiB
C#
using DocumentFormat.OpenXml.EMMA;
|
||
using FellowOakDicom;
|
||
using FellowOakDicom.Imaging;
|
||
using FellowOakDicom.Imaging.Render;
|
||
using FellowOakDicom.IO.Buffer;
|
||
using IRaCIS.Core.Application.Helper;
|
||
using MassTransit;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Hosting;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using SharpCompress.Common;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading.Tasks;
|
||
using System.Web;
|
||
using static IRaCIS.Core.Domain.Share.StaticData;
|
||
|
||
namespace IRaCIS.Core.Application.Service
|
||
{
|
||
|
||
/// <summary>
|
||
/// 项目影像后台下载,不打包
|
||
/// </summary>
|
||
/// <param name="_trialRepository"></param>
|
||
/// <param name="_oSSService"></param>
|
||
[ApiExplorerSettings(GroupName = "Common")]
|
||
public class TrialImageDownloadService(IRepository<Trial> _trialRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment) : BaseService
|
||
{
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 后端api swagger 下载项目影像
|
||
/// </summary>
|
||
/// <param name="trialId"></param>
|
||
/// <returns></returns>
|
||
[HttpPost]
|
||
[AllowAnonymous]
|
||
public async Task<IResponseOutput> DownloadTrialImage(Guid trialId)
|
||
{
|
||
//var subjectCodeList = new List<string>() { "05002", "07006", "07026" };
|
||
var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new
|
||
{
|
||
t.ResearchProgramNo,
|
||
|
||
VisitList = t.SubjectVisitList
|
||
//.Where(t=>subjectCodeList.Contains(t.Subject.Code))
|
||
.Select(sv => new
|
||
{
|
||
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
||
SubjectCode = sv.Subject.Code,
|
||
VisitName = sv.VisitName,
|
||
StudyList = sv.StudyList.Select(u => new
|
||
{
|
||
u.PatientId,
|
||
u.StudyTime,
|
||
u.StudyCode,
|
||
|
||
SeriesList = u.SeriesList.Where(t => t.IsReading).Select(z => new
|
||
{
|
||
z.Modality,
|
||
|
||
InstancePathList = z.DicomInstanceList.Where(t => t.IsReading).Select(k => new
|
||
{
|
||
k.Path
|
||
}).ToList()
|
||
})
|
||
|
||
}).ToList(),
|
||
|
||
NoneDicomStudyList = sv.NoneDicomStudyList.Where(t => t.IsReading).Select(nd => new
|
||
{
|
||
nd.Modality,
|
||
nd.StudyCode,
|
||
nd.ImageDate,
|
||
|
||
FileList = nd.NoneDicomFileList.Where(t => t.IsReading).Select(file => new
|
||
{
|
||
file.FileName,
|
||
file.Path,
|
||
file.FileType
|
||
}).ToList()
|
||
}).ToList()
|
||
}).ToList()
|
||
|
||
}).FirstOrDefault();
|
||
|
||
|
||
var count = downloadInfo.VisitList.SelectMany(t => t.NoneDicomStudyList).SelectMany(t => t.FileList).Count();
|
||
var count2 = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Count();
|
||
|
||
Console.WriteLine($"下载总数量:{count}+{count2}={count + count2}");
|
||
|
||
if (downloadInfo != null)
|
||
{
|
||
var downloadJobs = new List<Func<Task>>();
|
||
|
||
var rootFolder = @"E:\DownloadImage";
|
||
|
||
//var rootFolder = FileStoreHelper.GetDonwnloadImageFolder(_hostEnvironment);
|
||
|
||
// 获取无效字符(系统定义的)
|
||
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
|
||
|
||
// 用正则表达式替换所有非法字符为下划线或空字符
|
||
string pattern = $"[{Regex.Escape(invalidChars)}]";
|
||
|
||
var regexNo = Regex.Replace(downloadInfo.ResearchProgramNo, pattern, "_");
|
||
|
||
// 创建一个临时文件夹来存放文件
|
||
string trialFolderPath = Path.Combine(rootFolder, $"{regexNo}_{NewId.NextGuid()}");
|
||
Directory.CreateDirectory(trialFolderPath);
|
||
|
||
foreach (var visitItem in downloadInfo.VisitList)
|
||
{
|
||
if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
#region 处理 中心,受试者dicom non-dicom 文件夹层级
|
||
|
||
var siteFolderPath = Path.Combine(trialFolderPath, visitItem.TrialSiteCode);
|
||
if (!Directory.Exists(siteFolderPath))
|
||
{
|
||
Directory.CreateDirectory(siteFolderPath);
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
foreach (var studyInfo in visitItem.StudyList)
|
||
{
|
||
// 遍历 Series
|
||
foreach (var seriesInfo in studyInfo.SeriesList)
|
||
{
|
||
string studyDicomFolderPath = Path.Combine(siteFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}_DICOM", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}");
|
||
|
||
// 创建 影像 文件夹
|
||
Directory.CreateDirectory(studyDicomFolderPath);
|
||
|
||
// 遍历 InstancePathList
|
||
foreach (var instanceInfo in seriesInfo.InstancePathList)
|
||
{
|
||
// 复制文件到相应的文件夹
|
||
string destinationPath = Path.Combine(studyDicomFolderPath, Path.GetFileName(instanceInfo.Path));
|
||
|
||
|
||
//加入到下载任务里
|
||
downloadJobs.Add(() => _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath));
|
||
|
||
//下载到当前目录
|
||
//await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
foreach (var noneDicomStudy in visitItem.NoneDicomStudyList)
|
||
{
|
||
string studyNoneDicomFolderPath = Path.Combine(siteFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}_Non-DICOM", $"{noneDicomStudy.StudyCode}_{noneDicomStudy.ImageDate.ToString("yyyy-MM-dd")}_{noneDicomStudy.Modality}");
|
||
|
||
Directory.CreateDirectory(studyNoneDicomFolderPath);
|
||
|
||
foreach (var file in noneDicomStudy.FileList)
|
||
{
|
||
string destinationPath = Path.Combine(studyNoneDicomFolderPath, Path.GetFileName(file.FileName));
|
||
|
||
//加入到下载任务里
|
||
downloadJobs.Add(() => _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath));
|
||
//下载到当前目录
|
||
//await _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
#region 异步方式处理
|
||
|
||
int totalCount = downloadJobs.Count;
|
||
int downloadedCount = 0;
|
||
|
||
foreach (var job in downloadJobs)
|
||
{
|
||
try
|
||
{
|
||
await job();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Console.WriteLine($"下载失败: {ex.Message}");
|
||
}
|
||
|
||
downloadedCount++;
|
||
|
||
// 每处理50个,输出一次进度(或最后一个时也输出)
|
||
if (downloadedCount % 50 == 0 || downloadedCount == totalCount)
|
||
{
|
||
Console.WriteLine($"已下载 {downloadedCount} / {totalCount} 个文件,完成 {(downloadedCount * 100.0 / totalCount):F2}%");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 多线程测试
|
||
|
||
//const int batchSize = 15;
|
||
//int totalCount = downloadJobs.Count;
|
||
//int downloadedCount = 0;
|
||
|
||
//for (int i = 0; i < downloadJobs.Count; i += batchSize)
|
||
//{
|
||
// var batch = downloadJobs.Skip(i).Take(batchSize).Select(job => job());
|
||
|
||
// await Task.WhenAll(batch);
|
||
|
||
// downloadedCount += batch.Count();
|
||
|
||
// Console.WriteLine($"已下载 {downloadedCount} / {totalCount} 个文件,完成 {(downloadedCount * 100.0 / totalCount):F2}%");
|
||
//}
|
||
#endregion
|
||
|
||
}
|
||
|
||
return ResponseOutput.Ok();
|
||
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 下载影像 维护dir信息 并回传到OSS
|
||
/// </summary>
|
||
/// <param name="trialId"></param>
|
||
/// <returns></returns>
|
||
[HttpGet]
|
||
[AllowAnonymous]
|
||
public async Task<IResponseOutput> DownloadAndUploadTrialData(Guid trialId, [FromServices] IRepository<DicomInstance> _instanceRepository,
|
||
[FromServices] IRepository<DicomStudy> _studyRepository,
|
||
[FromServices] IRepository<DicomSeries> _seriesRepository)
|
||
{
|
||
var list = await _instanceRepository.Where(t => t.TrialId == trialId && t.SubjectVisitId == Guid.Parse("01000000-0a00-0242-bd20-08dcce543ded" ) && t.DicomStudy.ModalityForEdit == "IVUS")
|
||
.Select(t => new { t.SeriesId, t.StudyId, t.Id, t.Path }).ToListAsync();
|
||
|
||
int totalCount = list.Count;
|
||
int dealCount = 0;
|
||
foreach (var item in list)
|
||
{
|
||
|
||
var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
|
||
|
||
var dicomFile = DicomFile.Open(stream);
|
||
|
||
// 获取 Pixel Data 标签
|
||
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
|
||
|
||
//获取像素是否为封装形式
|
||
var syntax = dicomFile.Dataset.InternalTransferSyntax;
|
||
|
||
//对于封装像素的文件做转换
|
||
if (syntax.IsEncapsulated)
|
||
{
|
||
// 创建一个新的片段序列
|
||
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
|
||
// 获取每帧数据并封装为单独的片段
|
||
for (int n = 0; n < pixelData.NumberOfFrames; n++)
|
||
{
|
||
var frameData = pixelData.GetFrame(n);
|
||
newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
|
||
}
|
||
// 替换原有的片段序列
|
||
dicomFile.Dataset.AddOrUpdate(newFragments);
|
||
}
|
||
|
||
|
||
#region 获取dir信息 维护数据库三个表数据
|
||
|
||
var dirInfo = DicomDIRHelper.ReadDicomDIRInfo(dicomFile);
|
||
|
||
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id,
|
||
u => new DicomInstance()
|
||
{
|
||
IsEncapsulated= syntax.IsEncapsulated,
|
||
TransferSytaxUID = dirInfo.TransferSytaxUID,
|
||
SOPClassUID = dirInfo.SOPClassUID,
|
||
MediaStorageSOPClassUID = dirInfo.MediaStorageSOPClassUID,
|
||
MediaStorageSOPInstanceUID = dirInfo.MediaStorageSOPInstanceUID
|
||
}, false);
|
||
|
||
await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.SeriesId,
|
||
u => new DicomSeries()
|
||
{
|
||
DicomSeriesDate = dirInfo.DicomSeriesDate,
|
||
DicomSeriesTime = dirInfo.DicomSeriesTime,
|
||
}, false);
|
||
|
||
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.StudyId,
|
||
u => new DicomStudy()
|
||
{
|
||
DicomStudyDate = dirInfo.DicomStudyDate,
|
||
DicomStudyTime = dirInfo.DicomStudyTime
|
||
}, false);
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
//保存到内存流
|
||
using var memoryStream = new MemoryStream();
|
||
dicomFile.Save(memoryStream);
|
||
memoryStream.Position = 0;
|
||
|
||
//获取原始目录 和文件名
|
||
var folder = item.Path.Substring(0, item.Path.LastIndexOf('/')).TrimStart('/');
|
||
var fileName = Path.GetFileName(item.Path);
|
||
|
||
//dicomFile.Save($"download_{Guid.NewGuid()}");
|
||
|
||
await _oSSService.UploadToOSSAsync(memoryStream, folder, fileName, false);
|
||
|
||
dealCount++;
|
||
|
||
Console.WriteLine($"{DateTime.Now}已下载 {dealCount} / {totalCount} 个文件,完成 {(dealCount * 100.0 / totalCount):F2}%");
|
||
}
|
||
|
||
return ResponseOutput.Ok();
|
||
}
|
||
|
||
}
|
||
|
||
} |