using DocumentFormat.OpenXml.EMMA;
using DocumentFormat.OpenXml.Office2010.Excel;
using DocumentFormat.OpenXml.Office2013.Drawing.ChartStyle;
using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.Imaging.Render;
using FellowOakDicom.IO.Buffer;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
using MassTransit;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using SharpCompress.Common;
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using static IRaCIS.Core.Domain.Share.StaticData;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace IRaCIS.Core.Application.Service
{
///
/// 项目影像后台下载,不打包
///
///
///
[ApiExplorerSettings(GroupName = "Common")]
public class TrialImageDownloadService(IRepository _trialRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment) : BaseService
{
///
/// 后端api swagger 下载项目影像
///
///
///
[HttpPost]
[AllowAnonymous]
public async Task DownloadTrialImage(Guid trialId)
{
//var subjectCodeList = new List() { "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>();
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();
}
///
/// 下载影像 维护dir信息 并回传到OSS
///
///
///
[HttpGet]
[AllowAnonymous]
public async Task DownloadAndUploadTrialData(Guid trialId,
[FromServices] IRepository _instanceRepository,
[FromServices] IRepository _studyRepository,
[FromServices] IRepository _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();
}
///
/// 维护dir 需求新增的字段
///
///
///
///
///
///
public async Task TrialImageAddExtralField(Guid trialId,
[FromServices] IRepository _instanceRepository,
[FromServices] IRepository _studyRepository,
[FromServices] IRepository _seriesRepository)
{
// UPDATE DicomStudy
//SET DicomStudyDate = CONVERT(char(8), StudyTime, 112), --yyyyMMdd
// DicomStudyTime = REPLACE(CONVERT(char(8), StudyTime, 108), ':', ''); --HHmmss
// where DicomStudyDate = ''
//instance 找到传输语法为空的,然后分组
var seriesList = _instanceRepository.Where(t => t.TrialId == trialId && t.TransferSytaxUID == "")
//按照序列 和 NumberOfFrames 分组
.GroupBy(t => new { t.NumberOfFrames, t.SeriesId })
// 每个分组 取数据最小的一条
.Select(g => new { g.Key.SeriesId, g.Key.NumberOfFrames, g.OrderBy(t => t.FileSize).First().Path }).ToList();
foreach (var item in seriesList)
{
var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
var dicomFile = DicomFile.Open(stream);
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//读取需要维护的值
var transferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var mediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
var mediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
var sOPClassUID = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
//维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.SeriesId == item.SeriesId, t => new DicomInstance()
{
IsEncapsulated = syntax.IsEncapsulated,
TransferSytaxUID = transferSyntaxUID,
MediaStorageSOPClassUID = mediaStorageSOPClassUID,
SOPClassUID = sOPClassUID,
});
}
return ResponseOutput.Ok();
}
///
/// 下载已经删除的影像
///
///
///
[HttpPost]
[AllowAnonymous]
public async Task DownloadDeleteTrialImage(Guid trialId)
{
trialId = Guid.Parse("01000000-ac13-0242-6397-08dcd2d2a091");
var downloadInfo = _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => new
{
t.ResearchProgramNo,
VisitList = t.SubjectVisitList
.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.Select(z => new
{
z.Modality,
z.SeriesNumber,
InstancePathList = z.DicomInstanceList.Where(t => t.IsDeleted == true || t.DicomSerie.IsDeleted == true || t.IsReading == false || t.DicomSerie.IsReading == false).Select(k => new
{
k.Path,
k.FileSize
}).ToList()
})
}).ToList()
}).ToList()
}).FirstOrDefault();
var filesizes = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Sum(t => t.FileSize);
var count2 = downloadInfo.VisitList.SelectMany(t => t.StudyList).SelectMany(t => t.SeriesList).SelectMany(t => t.InstancePathList).Count();
Console.WriteLine($"下载总数量:{count2},总大小{filesizes}");
if (downloadInfo != null)
{
var downloadJobs = new List<(string Path, Func Job)>();
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)
{
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(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}", $"{seriesInfo.SeriesNumber}");
// 创建 影像 文件夹
Directory.CreateDirectory(studyDicomFolderPath);
// 遍历 InstancePathList
foreach (var instanceInfo in seriesInfo.InstancePathList)
{
// 复制文件到相应的文件夹
string destinationPath = Path.Combine(studyDicomFolderPath, Path.GetFileName(instanceInfo.Path));
//加入到下载任务里
downloadJobs.Add((instanceInfo.Path, () => _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath)));
//下载到当前目录
//await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath);
}
}
}
}
#region 异步方式处理
int totalCount = downloadJobs.Count;
int downloadedCount = 0;
// 在 trialFolderPath 下面放一个失败记录文件
string failedLogPath = Path.Combine(trialFolderPath, "failed_downloads.txt");
// 确保文件存在(如果之前有就清空)
File.WriteAllText(failedLogPath, "");
foreach (var job in downloadJobs)
{
try
{
await job.Job();
}
catch (Exception ex)
{
string errorMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 下载失败: {job.Path}, 错误: {ex.Message}\r\n";
Console.WriteLine(errorMessage);
await File.AppendAllTextAsync(failedLogPath, errorMessage);
}
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();
}
[AllowAnonymous]
public async Task ReadDicomDataWriteDB([FromServices] IRepository _instanceRepository)
{
var testPath = @"E:\WXT001";
var path = @"E:\WXT001";
var files = Directory.GetFiles(testPath, "*", SearchOption.AllDirectories)
// 只要没有后缀(Windows 显示类型是 .file)
.Where(f => string.IsNullOrEmpty(Path.GetExtension(f)))
.Where(f => Guid.TryParse(Path.GetFileNameWithoutExtension(f), out _))
.ToList();
Console.WriteLine($"找到 {files.Count} 个 DICOM 文件");
int total = files.Count;
int processed = 0;
double lastPercent = 0;
var options = new ParallelOptions { MaxDegreeOfParallelism = 12 };
// 输出文件路径
var outputFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info.txt");
var outputErrorFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info_error.txt");
// 用并发安全的写法(锁保护)
var fileLock = new object();
foreach (var file in files)
{
try
{
var id = Guid.Parse(Path.GetFileNameWithoutExtension(file));
var dicomFile = DicomFile.Open(file);
var dataset = dicomFile.Dataset;
var fileMeta = dicomFile.FileMetaInfo;
var syntax = dataset.InternalTransferSyntax;
//单位 设备 PatientId Visit 检查UId 帧数
var stationName = dataset.GetSingleValueOrDefault(DicomTag.StationName, string.Empty);
var institutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty);
var manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty);
//PatientID TrialCode_SubjectCode
//var patientID = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
//SubjectCode
var clinicalTrialSubjectID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialSubjectID, string.Empty);
//访视visitNum
var clinicalTrialTimePointID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialTimePointID, string.Empty);
var studyInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
var seriesInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
var sOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
var numberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
// 传输语法
var transferSyntaxUID = fileMeta.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var sOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
var mediaStorageSOPClassUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
var mediaStorageSOPInstanceUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
// 拼接一行 CSV 格式
var line = string.Join(",",
id,
stationName,
institutionName,
manufacturer,
clinicalTrialSubjectID,
clinicalTrialTimePointID,
studyInstanceUID,
seriesInstanceUID,
sOPInstanceUID,
numberOfFrames,
transferSyntaxUID,
sOPClassUID,
mediaStorageSOPClassUID,
mediaStorageSOPInstanceUID
);
await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
await _instanceRepository.BatchUpdateNoTrackingAsync(
t => t.Id == id,
t => new DicomInstance
{
IsEncapsulated = syntax.IsEncapsulated,
TransferSytaxUID = transferSyntaxUID,
MediaStorageSOPClassUID = mediaStorageSOPClassUID,
SOPClassUID = sOPClassUID,
MediaStorageSOPInstanceUID = mediaStorageSOPInstanceUID
}, false);
}
catch (Exception ex)
{
var errorMsg = $"{DateTime.Now}❌ {file} 解析失败: {ex.Message}";
Console.WriteLine(errorMsg);
await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
}
finally
{
var done = Interlocked.Increment(ref processed);
double percent = done * 100.0 / total;
// 只在进度提升 >= 1% 时打印
if (percent - lastPercent >= 5.0 || done == total)
{
lastPercent = percent;
Console.WriteLine($"{DateTime.Now} 进度: {done}/{total} ({percent:F2}%)");
}
}
}
return ResponseOutput.Ok();
}
#region 维护已经下载本地的数据
[AllowAnonymous]
public async Task ReadExcelData([FromServices] IRepository _instanceRepository)
{
var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\维护数据读取.xlsx");
rows = rows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
var outputErrorFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info_error.txt");
foreach (var item in rows)
{
try
{
await _instanceRepository.BatchUpdateNoTrackingAsync(
t => t.Id == item.InstanceId,
t => new DicomInstance
{
IsEncapsulated = item.IsEncapsulated,
TransferSytaxUID = item.TransferSyntaxUID,
MediaStorageSOPClassUID = item.MediaStorageSOPClassUID,
SOPClassUID = item.SOPClassUID,
MediaStorageSOPInstanceUID = item.MediaStorageSOPInstanceUID
}, false);
}
catch (Exception ex)
{
var errorMsg = $"{item.InstanceId} {DateTime.Now} 更新失败: {ex.Message}";
Console.WriteLine(errorMsg);
await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
}
}
return ResponseOutput.Ok();
}
public class DicomSOPInfo
{
public Guid InstanceId { get; set; }
public string TransferSyntaxUID { get; set; }
public string SOPClassUID { get; set; }
public string MediaStorageSOPClassUID { get; set; }
public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated => DicomTransferSyntax.Lookup(DicomUID.Parse(TransferSyntaxUID)).IsEncapsulated;
}
#endregion
#region 通过Excel 读取未下载的,边下载边维护数据
[AllowAnonymous]
public async Task WriteNeedDealData([FromServices] IRepository _instanceRepository)
{
#region 获取差集数据
//var rows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\维护数据读取.xlsx");
//rows = rows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
//var allRows = await MiniExcel.QueryAsync(@"C:\Users\hang\Desktop\AllData.xlsx");
//allRows = allRows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
//var needDealRows = allRows.Where(t => !rows.Select(c => c.InstanceId).Contains(t.InstanceId)).ToList();
//var outputFile = Path.Combine(@"D:\dicomWrite", $"{Guid.NewGuid()}_dicom_info.txt");
//foreach (var item in needDealRows)
//{
// var line = string.Join(",", item.InstanceId, item.Path);
// await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
//}
#endregion
var folder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment);
var needDealRows = await MiniExcel.QueryAsync(Path.Combine(folder, "needDownload.xlsx"));
needDealRows = needDealRows.Where(t => !string.IsNullOrEmpty(t.InstanceId.ToString())).ToList();
var outputFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info.txt");
var outputErrorFile = Path.Combine(folder, $"{Guid.NewGuid()}_dicom_info_error.txt");
int total = needDealRows.Count();
Console.WriteLine($"需要处理数量{total}");
int processed = 0;
double lastPercent = 0;
foreach (var item in needDealRows)
{
try
{
await using var stream = await _oSSService.GetStreamFromOSSAsync(item.Path);
var dicomFile = DicomFile.Open(stream);
var dataset = dicomFile.Dataset;
var fileMeta = dicomFile.FileMetaInfo;
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
var stationName = dataset.GetSingleValueOrDefault(DicomTag.StationName, string.Empty);
var institutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty);
var manufacturer = dataset.GetSingleValueOrDefault(DicomTag.Manufacturer, string.Empty);
//PatientID TrialCode_SubjectCode
//var patientID = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty);
//SubjectCode
var clinicalTrialSubjectID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialSubjectID, string.Empty);
//访视visitNum
var clinicalTrialTimePointID = dataset.GetSingleValueOrDefault(DicomTag.ClinicalTrialTimePointID, string.Empty);
var studyInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty);
var seriesInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty);
var sOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty);
var numberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
// 传输语法
var transferSyntaxUID = fileMeta.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var sOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
var mediaStorageSOPClassUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
var mediaStorageSOPInstanceUID = fileMeta.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
var line = string.Join(",",
item.InstanceId,
stationName,
institutionName,
manufacturer,
clinicalTrialSubjectID,
clinicalTrialTimePointID,
studyInstanceUID,
seriesInstanceUID,
sOPInstanceUID,
numberOfFrames,
transferSyntaxUID,
sOPClassUID,
mediaStorageSOPClassUID,
mediaStorageSOPInstanceUID
);
await File.AppendAllTextAsync(outputFile, line + Environment.NewLine);
//维护序列层级四个字段 后再用sql 维护study series 时间拆分 和 MediaStorageSOPInstanceUID
await _instanceRepository.BatchUpdateNoTrackingAsync(
t => t.Id == item.InstanceId,
t => new DicomInstance
{
IsEncapsulated = syntax.IsEncapsulated,
TransferSytaxUID = transferSyntaxUID,
MediaStorageSOPClassUID = mediaStorageSOPClassUID,
SOPClassUID = sOPClassUID,
MediaStorageSOPInstanceUID = mediaStorageSOPInstanceUID
}, false);
}
catch (Exception ex)
{
var errorMsg = $"{DateTime.Now}❌ 失败: {ex.Message}";
Console.WriteLine(errorMsg);
await File.AppendAllTextAsync(outputErrorFile, errorMsg + Environment.NewLine);
}
finally
{
processed++;
double percent = processed * 100.0 / total;
// 每提升 5% 或完成时输出
if (percent - lastPercent >= 5.0 || processed == total)
{
lastPercent = percent;
Console.WriteLine($"{DateTime.Now} 进度: {processed}/{total} ({percent:F2}%)");
}
}
}
return ResponseOutput.Ok();
}
public class NeedDealInstanceInfo
{
public Guid InstanceId { get; set; }
public string Path { get; set; }
}
#endregion
}
}