将dicom文件内处理拆成单方法,减缓内存占用测试
continuous-integration/drone/push Build is passing Details

Test_HIR_Net8
hang 2026-01-04 14:33:26 +08:00
parent 72d7136f1c
commit 88ded12f37
1 changed files with 168 additions and 89 deletions

View File

@ -1,4 +1,5 @@
using AutoMapper; using AutoMapper;
using DocumentFormat.OpenXml.Bibliography;
using DocumentFormat.OpenXml.ExtendedProperties; using DocumentFormat.OpenXml.ExtendedProperties;
using ExcelDataReader; using ExcelDataReader;
using FellowOakDicom; using FellowOakDicom;
@ -50,6 +51,7 @@ using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path; using Path = System.IO.Path;
@ -941,9 +943,9 @@ namespace IRaCIS.Core.API.Controllers
[HttpGet("download/GetPatientStudyBatchDownload")] [HttpGet("download/GetPatientStudyBatchDownload")]
public async Task<IActionResult> GetDownloadPatientStudyBatch( [FromServices] IOSSService _oSSService, public async Task<IActionResult> GetDownloadPatientStudyBatch([FromServices] IOSSService _oSSService,
[FromServices] IHubContext<DownloadHub, IDownloadClient> _downLoadHub, [FromServices] IHubContext<DownloadHub, IDownloadClient> _downLoadHub,
//[FromServices] IPatientService _patientService, //[FromServices] IPatientService _patientService,
[FromServices] IServiceScopeFactory _serviceScopeFactory, [FromServices] IServiceScopeFactory _serviceScopeFactory,
[FromQuery] PatientImageDownloadCommand inCommand) [FromQuery] PatientImageDownloadCommand inCommand)
{ {
@ -958,7 +960,7 @@ namespace IRaCIS.Core.API.Controllers
return (result.Data, (SubejctVisitDownload)result.OtherData); return (result.Data, (SubejctVisitDownload)result.OtherData);
} }
}); });
long receivedSize = 0; long receivedSize = 0;
long receivedCount = 0; long receivedCount = 0;
long totalSize = downloadInfo.ImageSize; long totalSize = downloadInfo.ImageSize;
@ -1091,67 +1093,79 @@ namespace IRaCIS.Core.API.Controllers
if (instance.IsEncapsulated) if (instance.IsEncapsulated)
{ {
#region 将多帧合并为一帧 #region 将多帧合并为一帧-在方法里,占用内存,拆成单独方法,不占用内存
try //try
{ //{
// 如果你是从 stream 打开 // // 如果你是从 stream 打开
var dicomFile = await DicomFile.OpenAsync(source); // var dicomFile = await DicomFile.OpenAsync(source);
//获取像素是否为封装形式 // //获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax; // var syntax = dicomFile.Dataset.InternalTransferSyntax;
//对于封装像素的文件做转换 // //对于封装像素的文件做转换
if (syntax.IsEncapsulated) // if (syntax.IsEncapsulated)
{ // {
// 获取 Pixel Data 标签 // // 获取 Pixel Data 标签
var pixelData = DicomPixelData.Create(dicomFile.Dataset); // var pixelData = DicomPixelData.Create(dicomFile.Dataset);
// 创建一个新的片段序列 // // 创建一个新的片段序列
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData); // var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
// 获取每帧数据并封装为单独的片段 // // 获取每帧数据并封装为单独的片段
for (int n = 0; n < pixelData.NumberOfFrames; n++) // for (int n = 0; n < pixelData.NumberOfFrames; n++)
{ // {
var frameData = pixelData.GetFrame(n); // var frameData = pixelData.GetFrame(n);
newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data)); // newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
} // }
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData); // var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
var originOffsetTable = frag?.OffsetTable; // var originOffsetTable = frag?.OffsetTable;
newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray()); // newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray());
// 替换原有的片段序列 // // 替换原有的片段序列
dicomFile.Dataset.AddOrUpdate(newFragments); // dicomFile.Dataset.AddOrUpdate(newFragments);
} // }
await dicomFile.SaveAsync(entryStream); // await dicomFile.SaveAsync(entryStream);
} //}
catch (Exception ex) //catch (Exception ex)
{ //{
Log.Logger.Warning($"处理多帧合并{instance.Path}失败,以原始文件下载: {ex.Message}"); // Log.Logger.Warning($"处理多帧合并{instance.Path}失败,以原始文件下载: {ex.Message}");
try // try
{ // {
await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path); // await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path);
await source2.CopyToAsync(entryStream, 32 * 1024, abortToken); // await source2.CopyToAsync(entryStream, 32 * 1024, abortToken);
} // }
catch (Exception ex2) // catch (Exception ex2)
{ // {
Log.Logger.Warning($"原始文件下载{instance.Path}失败: {ex2.Message}"); // Log.Logger.Warning($"原始文件下载{instance.Path}失败: {ex2.Message}");
} // }
} //}
#endregion #endregion
var ok = await TryWriteMergedDicomAsync(
() => _oSSService.GetStreamFromOSSAsync(instance.Path),
entryStream
);
if (!ok)
{
await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path);
await source2.CopyToAsync(entryStream, 32 * 1024, abortToken);
}
} }
else else
{ {
@ -1216,6 +1230,55 @@ namespace IRaCIS.Core.API.Controllers
} }
private static async Task<bool> TryWriteMergedDicomAsync(
Func<Task<Stream>> sourceFactory,
Stream output)
{
try
{
await using var source = await sourceFactory();
// 如果你是从 stream 打开
var dicomFile = await DicomFile.OpenAsync(source);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//对于封装像素的文件做转换
if (syntax.IsEncapsulated)
{
// 获取 Pixel Data 标签
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
// 创建一个新的片段序列
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));
}
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
var originOffsetTable = frag?.OffsetTable;
newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray());
// 替换原有的片段序列
dicomFile.Dataset.AddOrUpdate(newFragments);
}
await dicomFile.SaveAsync(output);
return true;
}
catch (Exception ex)
{
// 只记录,不传播
Log.Logger.Warning($"TryWriteMergedDicomAsync failed: {ex.Message}");
return false;
}
}
[HttpPost("download/PatientStudyBatchDownloadForm")] [HttpPost("download/PatientStudyBatchDownloadForm")]
public async Task<IActionResult> DownloadPatientStudyBatchForm([FromServices] IPatientService _patientService, [FromServices] IOSSService _oSSService, public async Task<IActionResult> DownloadPatientStudyBatchForm([FromServices] IPatientService _patientService, [FromServices] IOSSService _oSSService,
@ -1883,10 +1946,10 @@ namespace IRaCIS.Core.API.Controllers
[HttpGet("download/VisitImageDownload")] [HttpGet("download/VisitImageDownload")]
public async Task<IActionResult> VisitImageDownload( [FromServices] IOSSService _oSSService, public async Task<IActionResult> VisitImageDownload([FromServices] IOSSService _oSSService,
[FromServices] IHubContext<DownloadHub, IDownloadClient> _downLoadHub, [FromServices] IHubContext<DownloadHub, IDownloadClient> _downLoadHub,
//[FromServices] IRepository<Trial> _trialRepository, //[FromServices] IRepository<Trial> _trialRepository,
//[FromServices] IPatientService _patientService, //[FromServices] IPatientService _patientService,
[FromServices] IServiceScopeFactory _serviceScopeFactory, // 注入ServiceScopeFactory [FromServices] IServiceScopeFactory _serviceScopeFactory, // 注入ServiceScopeFactory
[FromQuery] VisitImageDownloadCommand inCommand) [FromQuery] VisitImageDownloadCommand inCommand)
{ {
@ -2044,61 +2107,77 @@ namespace IRaCIS.Core.API.Controllers
if (instance.IsEncapsulated) if (instance.IsEncapsulated)
{ {
#region 将多帧合并为一帧 #region 将多帧合并为一帧-在方法里,占用内存,拆成单独方法,不占用内存
try
{
// 如果你是从 stream 打开
var dicomFile = await DicomFile.OpenAsync(source);
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//对于封装像素的文件做转换
if (syntax.IsEncapsulated)
{
// 获取 Pixel Data 标签
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
// 创建一个新的片段序列 //try
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData); //{
// 获取每帧数据并封装为单独的片段 // // 如果你是从 stream 打开
for (int n = 0; n < pixelData.NumberOfFrames; n++) // var dicomFile = await DicomFile.OpenAsync(source);
{
var frameData = pixelData.GetFrame(n);
newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
}
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData); // //获取像素是否为封装形式
// var syntax = dicomFile.Dataset.InternalTransferSyntax;
var originOffsetTable = frag?.OffsetTable; // //对于封装像素的文件做转换
// if (syntax.IsEncapsulated)
// {
// // 获取 Pixel Data 标签
// var pixelData = DicomPixelData.Create(dicomFile.Dataset);
newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray()); // // 创建一个新的片段序列
// 替换原有的片段序列 // var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
dicomFile.Dataset.AddOrUpdate(newFragments); // // 获取每帧数据并封装为单独的片段
} // for (int n = 0; n < pixelData.NumberOfFrames; n++)
// {
// var frameData = pixelData.GetFrame(n);
// newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
// }
await dicomFile.SaveAsync(entryStream); // var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
} // var originOffsetTable = frag?.OffsetTable;
catch (Exception ex)
{
Log.Logger.Warning($"处理多帧合并{instance.Path}失败: {ex.Message}"); // newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray());
// // 替换原有的片段序列
// dicomFile.Dataset.AddOrUpdate(newFragments);
// }
try // await dicomFile.SaveAsync(entryStream);
{
await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path);
await source2.CopyToAsync(entryStream, 32 * 1024, abortToken); //}
} //catch (Exception ex)
catch (Exception ex2) //{
{
Log.Logger.Warning($"原始文件下载{instance.Path}失败: {ex2.Message}"); // Log.Logger.Warning($"处理多帧合并{instance.Path}失败,以原始文件下载: {ex.Message}");
}
} // try
// {
// await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path);
// await source2.CopyToAsync(entryStream, 32 * 1024, abortToken);
// }
// catch (Exception ex2)
// {
// Log.Logger.Warning($"原始文件下载{instance.Path}失败: {ex2.Message}");
// }
//}
#endregion #endregion
var ok = await TryWriteMergedDicomAsync(
() => _oSSService.GetStreamFromOSSAsync(instance.Path),
entryStream
);
if (!ok)
{
await using var source2 = await _oSSService.GetStreamFromOSSAsync(instance.Path);
await source2.CopyToAsync(entryStream, 32 * 1024, abortToken);
}
} }
else else
{ {