Các tổ chức chăm sóc sức khỏe ngày càng cần các giải pháp tự động để mất nhận dạng dữ liệu hình ảnh y tế. Trong hướng dẫn này, bạn sẽ tìm hiểu làm thế nào để xây dựng một DICOM anonimization microservice trong ASP.NET Core mà nhận được các tập tin DICO thông qua HTTP, anonymizes chúng bằng cách sử dụng các hồ sơ có thể cấu hình, và trả về kết quả không xác định.

Bảng nội dung

Tổng quan kiến trúc

Microservice theo một kiến trúc lớp với sự phân biệt rõ ràng của mối quan tâm. lớp API xử lý các yêu cầu HTTP và câu trả lời thông qua các bộ điều khiển ASP.NET Core. Layer dịch vụ chứa logic kinh doanh sử dụng Aspose.Medical Anonymizer. The configuration layer manages profile settings and application options. Optional components include storage for logging and queuing for batch processing.

Kiến trúc này cho phép quy mô ngang, kiểm tra dễ dàng và tích hợp đơn giản với các hệ thống thông tin bệnh viện và máy chủ PACS.

Thiết lập dự án

Bắt đầu bằng cách tạo một dự án mới ASP.NET Core Web API và cài đặt gói yêu cầu:

dotnet new webapi -n DicomAnonymizationService
cd DicomAnonymizationService
dotnet add package Aspose.Medical

Thiết lập dự án cho file upload lớn trong 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();

Thực hiện Layer Service

Tạo một giao diện và thực hiện cho dịch vụ anonymization:

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);
    }
}

Xây dựng bộ điều khiển API

Tạo một bộ điều khiển quản lý các bản tải file và trả về các tập tin DICOM vô danh:

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);
    }
}

Các hồ sơ có thể cấu hình

Cho phép các hồ sơ được cấu hình qua appsettings.json:

{
  "AnonymizationProfiles": {
    "default": "basic",
    "profiles": {
      "basic": ["BasicProfile"],
      "research": ["BasicProfile", "RetainPatientChars"],
      "internal": ["RetainUIDs", "RetainDeviceIdent"],
      "clean": ["CleanGraph", "CleanDesc"],
      "maximum": ["BasicProfile", "CleanGraph", "CleanDesc"]
    }
  }
}

Tạo một lớp cấu hình và cập nhật dịch vụ để đọc từ cài đặt:

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"));

Các vấn đề an ninh {#cách bảo mật}

Khi triển khai dịch vụ này, thực hiện các biện pháp bảo mật này.

Cung cấp HTTPS để đảm bảo tất cả các thông tin được mã hóa trong quá trình giao thông.Cài đặt TLS 1.2 hoặc cao hơn và chuyển hướng Http sang https.

Tự xác thực thực hiện bằng cách sử dụng mã JWT hoặc khóa API để hạn chế quyền truy cập cho khách hàng được ủy quyền:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class AnonymizeController : ControllerBase
{
    // Controller code...
}
  • Tránh lưu trữ các tệp gốc** không được mã hóa trên ổ đĩa. xử lý các tập tin trong bộ nhớ khi có thể, và nếu bảo quản tạm thời là cần thiết, sử dụng khối lượng mã hoá.

  • Khả năng đăng ký kiểm toán** để theo dõi tất cả các hoạt động nhận dạng, bao gồm những người đã yêu cầu và khi:

_logger.LogInformation(
    "Anonymization completed - User: {User}, Profile: {Profile}, FileSize: {Size}",
    User.Identity?.Name, profile, file.Length);

Tối ưu hóa hiệu suất - Performance Optimization

Một số kỹ thuật có thể cải thiện hiệu suất cho các kịch bản tốc độ cao.

** Sử dụng danh tính tại chỗ** khi bạn không cần lưu trữ dữ liệu ban đầu trong bộ nhớ:

anonymizer.AnonymizeInPlace(dcm);
  • Khả năng xử lý song song** cho các hoạt động nhóm:
[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...
}

Hành động yêu cầu quwing cho các gói rất lớn bằng cách sử dụng một quing thông điệp như RabbitMQ hoặc Azure Service Bus để xử lý các đỉnh tải mà không gây phiền nhiễu dịch vụ.

Kết luận = kết luận

Bây giờ bạn có một kế hoạch đầy đủ để xây dựng một DICOM anonimization microservice trong ASP.NET Core. Dịch vụ chấp nhận các tập tin DICO thông qua HTTP, áp dụng cấu hình hồ sơ anonymization bằng cách sử dụng Aspose.Medical, và trả về các tệp không được xác định sẵn sàng cho nghiên cứu hoặc chia sẻ.

Các vấn đề quan trọng bao gồm việc sử dụng kiến trúc lớp để duy trì, thực hiện các hồ sơ có thể cấu hình để linh hoạt, đảm bảo dịch vụ với xác thực và mã hóa, và tối ưu hóa hiệu suất với xử lý song song.

Đối với mã nguồn đầy đủ và các ví dụ bổ sung, hãy truy cập ASPOSE - Tài liệu y tếĐể thử lửa, Nhận giấy phép tạm thời miễn phí.

More in this category