건강 관리 조직은 점점 더 자동화 된 솔루션을 필요로 의학 이미지 데이터를 탐지합니다.이 가이드에서는 ASP.NET 코어**에서 DICOM 파일을 HTTP를 통해 수신하고, 구성 가능한 프로필을 사용하여 그들을 익명화시키고, 잘못 식별 된 결과를 반환하는 **Dicom 애니메이션 마이크로 서비스를 구축하는 방법을 배우게됩니다.
테이블 콘텐츠
아키텍처 전체 보기
마이크로 서비스는 걱정의 명확한 분리와 층 아키텍처를 따릅니다. API 레이어는 ASP.NET 코어 컨트롤러를 통해 HTTP 요청 및 응답을 처리합니다. 서비스 레이아에는 Aspose.Medical Anonymizer를 사용하여 비즈니스 논리가 포함되어 있습니다. 구성 레이아가 프로필 설정을 관리하고 애플리케이션 옵션을 관리 합니다.
이 아키텍처는 수평 스케일링, 쉬운 테스트 및 병원 정보 시스템 및 PACS 서버와 간단한 통합을 가능하게합니다.
프로젝트 설정
새로운 ASP.NET Core Web API 프로젝트를 만들고 필요한 패키지를 설치하여 시작하십시오.
dotnet new webapi -n DicomAnonymizationService
cd DicomAnonymizationService
dotnet add package Aspose.Medical
큰 파일 업로드에 대한 프로젝트 설정 Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Configure for large DICOM files
builder.WebHost.ConfigureKestrel(options =>
{
options.Limits.MaxRequestBodySize = 500_000_000; // 500MB
});
builder.Services.Configure<FormOptions>(options =>
{
options.MultipartBodyLengthLimit = 500_000_000;
});
// Register services
builder.Services.AddScoped<IDicomAnonymizationService, DicomAnonymizationService>();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
서비스 레이어 구현
익명화 서비스에 대한 인터페이스 및 구현을 만드십시오 :
using Aspose.Medical.Dicom;
using Aspose.Medical.Dicom.Anonymization;
public interface IDicomAnonymizationService
{
Task<byte[]> AnonymizeAsync(Stream inputStream, string? profileName = null);
Task<byte[]> AnonymizeAsync(byte[] inputBytes, string? profileName = null);
}
public class DicomAnonymizationService : IDicomAnonymizationService
{
private readonly ILogger<DicomAnonymizationService> _logger;
private readonly Dictionary<string, ConfidentialityProfileOptions> _profiles;
public DicomAnonymizationService(ILogger<DicomAnonymizationService> logger)
{
_logger = logger;
_profiles = new Dictionary<string, ConfidentialityProfileOptions>
{
["basic"] = ConfidentialityProfileOptions.BasicProfile,
["research"] = ConfidentialityProfileOptions.BasicProfile |
ConfidentialityProfileOptions.RetainPatientChars,
["internal"] = ConfidentialityProfileOptions.RetainUIDs |
ConfidentialityProfileOptions.RetainDeviceIdent,
["clean"] = ConfidentialityProfileOptions.CleanGraph |
ConfidentialityProfileOptions.CleanDesc
};
}
public async Task<byte[]> AnonymizeAsync(Stream inputStream, string? profileName = null)
{
using var memoryStream = new MemoryStream();
await inputStream.CopyToAsync(memoryStream);
return await AnonymizeAsync(memoryStream.ToArray(), profileName);
}
public Task<byte[]> AnonymizeAsync(byte[] inputBytes, string? profileName = null)
{
return Task.Run(() =>
{
// Create anonymizer with selected profile
Anonymizer anonymizer = CreateAnonymizer(profileName);
// Load DICOM from bytes
using var inputStream = new MemoryStream(inputBytes);
DicomFile dcm = DicomFile.Open(inputStream);
// Anonymize
DicomFile anonymizedDcm = anonymizer.Anonymize(dcm);
// Write to output stream
using var outputStream = new MemoryStream();
anonymizedDcm.Save(outputStream);
_logger.LogInformation("Anonymized DICOM file using profile: {Profile}",
profileName ?? "default");
return outputStream.ToArray();
});
}
private Anonymizer CreateAnonymizer(string? profileName)
{
if (string.IsNullOrEmpty(profileName) || !_profiles.ContainsKey(profileName.ToLower()))
{
return new Anonymizer();
}
var options = _profiles[profileName.ToLower()];
var profile = ConfidentialityProfile.CreateDefault(options);
return new Anonymizer(profile);
}
}
API 컨트롤러 건설
파일 업로드를 관리하고 익명의 DICOM 파일을 반환하는 컨트롤러를 만드십시오 :
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class AnonymizeController : ControllerBase
{
private readonly IDicomAnonymizationService _anonymizationService;
private readonly ILogger<AnonymizeController> _logger;
public AnonymizeController(
IDicomAnonymizationService anonymizationService,
ILogger<AnonymizeController> logger)
{
_anonymizationService = anonymizationService;
_logger = logger;
}
/// <summary>
/// Anonymize a single DICOM file
/// </summary>
[HttpPost]
[RequestSizeLimit(500_000_000)]
public async Task<IActionResult> AnonymizeFile(
IFormFile file,
[FromQuery] string? profile = null)
{
if (file == null || file.Length == 0)
{
return BadRequest("No file uploaded");
}
try
{
_logger.LogInformation("Processing file: {FileName}, Size: {Size}",
file.FileName, file.Length);
using var stream = file.OpenReadStream();
byte[] anonymizedBytes = await _anonymizationService.AnonymizeAsync(stream, profile);
string outputFileName = $"anonymized_{file.FileName}";
return File(anonymizedBytes, "application/dicom", outputFileName);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error anonymizing file: {FileName}", file.FileName);
return StatusCode(500, $"Anonymization failed: {ex.Message}");
}
}
/// <summary>
/// Anonymize DICOM data from request body
/// </summary>
[HttpPost("stream")]
[RequestSizeLimit(500_000_000)]
public async Task<IActionResult> AnonymizeStream([FromQuery] string? profile = null)
{
try
{
byte[] anonymizedBytes = await _anonymizationService.AnonymizeAsync(
Request.Body, profile);
return File(anonymizedBytes, "application/dicom", "anonymized.dcm");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error anonymizing stream");
return StatusCode(500, $"Anonymization failed: {ex.Message}");
}
}
/// <summary>
/// List available anonymization profiles
/// </summary>
[HttpGet("profiles")]
public IActionResult GetProfiles()
{
var profiles = new[]
{
new { name = "basic", description = "Standard anonymization removing most identifiers" },
new { name = "research", description = "Retains patient characteristics for research" },
new { name = "internal", description = "Retains UIDs for internal tracking" },
new { name = "clean", description = "Removes burned-in graphics and descriptions" }
};
return Ok(profiles);
}
}
설정할 수 있는 프로파일(Configurable Profiles)
프로파일을 구성할 수 있도록 허용 appsettings.json:
{
"AnonymizationProfiles": {
"default": "basic",
"profiles": {
"basic": ["BasicProfile"],
"research": ["BasicProfile", "RetainPatientChars"],
"internal": ["RetainUIDs", "RetainDeviceIdent"],
"clean": ["CleanGraph", "CleanDesc"],
"maximum": ["BasicProfile", "CleanGraph", "CleanDesc"]
}
}
}
구성 클래스를 만들고 설정에서 읽기 위해 서비스를 업데이트하십시오 :
public class AnonymizationProfileOptions
{
public string Default { get; set; } = "basic";
public Dictionary<string, string[]> Profiles { get; set; } = new();
}
// In Program.cs
builder.Services.Configure<AnonymizationProfileOptions>(
builder.Configuration.GetSection("AnonymizationProfiles"));
보안에 대한 의견(#Security Considerations)
이 서비스를 실행할 때, 이러한 보안 조치를 구현합니다.
**HTTPS를 활성화하여 모든 통신이 트랜지스에서 암호화되어 있는지 확인하십시오.TLS 1.2 이상을 설정하고 HTTP를 http로 리디렉션합니다.
**JWT 토큰 또는 API 키를 사용하여 권한을 부여받은 고객에 대한 액세스를 제한합니다.
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class AnonymizeController : ControllerBase
{
// Controller code...
}
- 디스크에 암호화되지 않은 원본 파일을 저장하는 것을 피하십시오.이 파일은 가능한 경우 메모리로 처리하고, 일시적인 저장이 필요하다면 암포화 된 볼륨을 사용합니다.
**모든 익명화 작업을 추적할 수 있는 감사 로그링을 허용하며, 누가 요청했는지 포함하여 다음과 같은 경우:
_logger.LogInformation(
"Anonymization completed - User: {User}, Profile: {Profile}, FileSize: {Size}",
User.Identity?.Name, profile, file.Length);
성능 최적화(Performance Optimization)
여러 가지 기술은 높은 속도 시나리오에서 성능을 향상시킬 수 있습니다.
** 원본 데이터를 기억에 보관할 필요가 없을 때 현장 익명화*를 사용하십시오.
anonymizer.AnonymizeInPlace(dcm);
** 배치 작업을 위한 병렬 처리 가능**:
[HttpPost("batch")]
public async Task<IActionResult> AnonymizeBatch(List<IFormFile> files)
{
var results = await Parallel.ForEachAsync(
files,
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
async (file, ct) =>
{
using var stream = file.OpenReadStream();
return await _anonymizationService.AnonymizeAsync(stream);
});
// Return results...
}
**RabbitMQ 또는 Azure Service Bus와 같은 메시지 퀴를 사용하여 매우 큰 배치에 대한 쿼링 요청을 구현하여 서비스가 과장되지 않고 로드 스파이크를 처리합니다.
결론을 내리며
이 서비스는 HTTP를 통해 DICOM 파일을 수락하고, Aspose.Medical을 사용하여 구성 가능한 익명화 프로필을 적용하며, 연구 또는 공유를 위해 준비된 데 식별 된 파일이 반환됩니다.
핵심 요소는 유지 보수를 위해 레이어링 아키텍처를 사용하고, 유연성을 위해 구성 가능한 프로파일을 구현하며, 인증 및 암호화로 서비스를 보장하고 병렬 처리로 성능을 최적화하는 것입니다.
전체 출처 코드 및 추가 예제는 다음을 참조하십시오. ASPOSE - 의학 문서화재를 시도하려면, 무료 임시 라이센스를 받으십시오..