오늘날의 디지털 시대에, 효율적인 이미지 처리 웹 응용 프로그램 및 APIs에 중요합니다. 이미지 관리의 핵심 측면 중 하나는 압축이며, 이는 품질에 상당한 위협없이 파일 크기를 줄이는 데 도움이됩니다.이 가이드는 Aspose.Imaging for .NET을 사용하여 역동적 인 이미지 압력 API를 구축함으로써 당신을 도와줍니다.

Aspose.Imaging은 .NET에서 이미지 작업을위한 강력한 라이브러리입니다.그것은 많은 형식을 지원하고 손실없는 (JPEG) 및 PNG) 작업 흐름을 포함하여 견고한 조작 기능을 제공합니다.우리는 효율적이고 확장 가능한 압축 서비스를 구축하기 위해 그것을 활용할 것입니다.

무엇을 만들 것인가

    • 최종 포인트 : POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • 입력: 여러 부분 파일 (사진), 형식 / 품질 / 크기에 대한 선택적 쿼리 매개 변수
  • 출력: 올바른 이미지와 함께 압축된 이미지 스트림 Content-Type Caching 헤드셋
  • 안전: 콘텐츠 유형 인증, 크기 제한 및 보관된 디코드/엔코드는

원칙

  • 넷 8 (또는 .NET 6+)
  • ASP.NET 코어 웹 API 프로젝트
  • 니게트 : Aspose.Imaging
  • 선택: 애플리케이션 스타트업에서 라이센스 이니셔티브 (허가된 건물을 사용하는 경우)

프로젝트 구조 (최소)

/Controllers
  ImageController.cs
/Services
  ImageCompressionService.cs
/Models
  CompressionRequest.cs
Program.cs
appsettings.json

완전한 예제 (서비스 + 컨트롤러)

장소 주인 이름 공간을 프로젝트의 이름 영역으로 대체하십시오.

/Models/CompressionRequest.cs

namespace ImageApi.Models;

public sealed class CompressionRequest
{
    // "jpeg" or "png"
    public string Format { get; init; } = "jpeg";

    // 1..100 (applies to JPEG only; PNG is lossless)
    public int? Quality { get; init; } = 80;

    // Optional resize bounds; image is resized preserving aspect ratio if either is provided.
    public int? MaxWidth { get; init; }
    public int? MaxHeight { get; init; }

    // If true, strip metadata (EXIF, IPTC) where applicable to reduce size further.
    public bool StripMetadata { get; init; } = true;

    // Guardrails
    public void Validate()
    {
        var fmt = Format?.ToLowerInvariant();
        if (fmt is not "jpeg" and not "png")
            throw new ArgumentException("Unsupported format. Use 'jpeg' or 'png'.");

        if (Quality is { } q && (q < 1 || q > 100))
            throw new ArgumentException("Quality must be between 1 and 100.");

        if (MaxWidth is { } w && w <= 0) throw new ArgumentException("MaxWidth must be positive.");
        if (MaxHeight is { } h && h <= 0) throw new ArgumentException("MaxHeight must be positive.");
    }
}

/Services/ImageCompressionService.cs

using Aspose.Imaging;
using Aspose.Imaging.ImageOptions;
using ImageApi.Models;

namespace ImageApi.Services;

public interface IImageCompressionService
{
    Task<(MemoryStream output, string contentType, string fileExt)> CompressAsync(
        Stream input, CompressionRequest req, CancellationToken ct = default);
}

public sealed class ImageCompressionService : IImageCompressionService
{
    private readonly ILogger<ImageCompressionService> _logger;

    public ImageCompressionService(ILogger<ImageCompressionService> logger)
    {
        _logger = logger;
    }

    public async Task<(MemoryStream output, string contentType, string fileExt)> CompressAsync(
        Stream input, CompressionRequest req, CancellationToken ct = default)
    {
        req.Validate();

        // Defensive copy to a seekable stream
        var inbound = new MemoryStream();
        await input.CopyToAsync(inbound, ct).ConfigureAwait(false);
        inbound.Position = 0;

        // Load image via Aspose.Imaging
        using var image = Image.Load(inbound);

        // Optional: strip metadata (where applicable)
        if (req.StripMetadata)
        {
            TryStripMetadata(image);
        }

        // Optional resize (preserve aspect ratio)
        if (req.MaxWidth.HasValue || req.MaxHeight.HasValue)
        {
            ResizeInPlace(image, req.MaxWidth, req.MaxHeight);
        }

        // Choose encoder and options
        string fmt = req.Format.ToLowerInvariant();
        var (options, contentType, ext) = BuildOptions(fmt, req.Quality);

        // Save to output
        var output = new MemoryStream();
        image.Save(output, options);
        output.Position = 0;

        _logger.LogInformation("Compressed image to {Bytes} bytes as {Ext}", output.Length, ext);
        return (output, contentType, ext);
    }

    private static void ResizeInPlace(Image image, int? maxW, int? maxH)
    {
        var w = image.Width;
        var h = image.Height;

        double scaleW = maxW.HasValue ? (double)maxW.Value / w : 1.0;
        double scaleH = maxH.HasValue ? (double)maxH.Value / h : 1.0;
        double scale = Math.Min(scaleW, scaleH);

        if (scale < 1.0)
        {
            int newW = Math.Max(1, (int)Math.Round(w * scale));
            int newH = Math.Max(1, (int)Math.Round(h * scale));
            image.Resize(newW, newH);
        }
    }

    private static (ImageOptionsBase options, string contentType, string ext) BuildOptions(string fmt, int? quality)
    {
        switch (fmt)
        {
            case "jpeg":
            {
                var q = quality ?? 80;
                var jpeg = new JpegOptions { Quality = q };
                return (jpeg, "image/jpeg", "jpg");
            }
            case "png":
            {
                // PNG is lossless; using defaults ensures broad compatibility.
                // Many PNG tunables exist, but defaults are safe and effective.
                var png = new PngOptions();
                return (png, "image/png", "png");
            }
            default:
                throw new ArgumentOutOfRangeException(nameof(fmt), "Unsupported format.");
        }
    }

    private static void TryStripMetadata(Image image)
    {
        try
        {
            // Not every format exposes EXIF/IPTC the same way; a best-effort clear:
            if (image is RasterImage raster)
            {
                raster.RemoveAllFonts();
                raster.SetPropertyItems(Array.Empty<PropertyItem>());
            }
        }
        catch
        {
            // Non-fatal; ignore if format doesn't support these operations
        }
    }
}

Notes

  • JpegOptions.Quality (1-100) 손실 압축을 제어합니다.
  • PNG 결함은 일반적으로 첫 번째 버전을 위해 괜찮습니다; 당신이 추가 작은 P NG를 필요로하는 경우, 당신은 나중에 고급 튜닝을 추가 할 수 있습니다.
  • TryStripMetadata 그것은 최선의 접근 방식입니다; 메타 데이터 APIs는 출처 형식에 따라 다릅니다.

/Controllers/ImageController.cs

using ImageApi.Models;
using ImageApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace ImageApi.Controllers;

[ApiController]
[Route("api/images")]
public sealed class ImageController : ControllerBase
{
    private static readonly HashSet<string> AllowedContentTypes = new(StringComparer.OrdinalIgnoreCase)
    {
        "image/jpeg", "image/png", "image/gif", "image/webp", "image/bmp", "image/tiff"
    };

    private readonly IImageCompressionService _svc;
    private readonly ILogger<ImageController> _logger;

    public ImageController(IImageCompressionService svc, ILogger<ImageController> logger)
    {
        _svc = svc;
        _logger = logger;
    }

    // POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
    [HttpPost("compress")]
    [RequestSizeLimit(25_000_000)] // 25 MB cap; adjust to your needs
    public async Task<IActionResult> Compress(
        [FromQuery] string? format,
        [FromQuery] int? quality,
        [FromQuery] int? maxWidth,
        [FromQuery] int? maxHeight,
        [FromQuery] bool stripMetadata = true,
        IFormFile? file = null,
        CancellationToken ct = default)
    {
        if (file is null || file.Length == 0)
            return BadRequest("No file uploaded.");

        if (!AllowedContentTypes.Contains(file.ContentType))
            return BadRequest("Unsupported content type. Upload a common raster image (JPEG, PNG, GIF, WebP, BMP, TIFF).");

        var req = new CompressionRequest
        {
            Format = string.IsNullOrWhiteSpace(format) ? "jpeg" : format!,
            Quality = quality,
            MaxWidth = maxWidth,
            MaxHeight = maxHeight,
            StripMetadata = stripMetadata
        };

        await using var input = file.OpenReadStream();
        var (output, contentType, ext) = await _svc.CompressAsync(input, req, ct);

        // Strong caching for immutable responses (tune for your app/CDN)
        Response.Headers.CacheControl = "public,max-age=31536000,immutable";

        return File(output, contentType, fileDownloadName: BuildDownloadName(file.FileName, ext));
    }

    private static string BuildDownloadName(string originalName, string newExt)
    {
        var baseName = Path.GetFileNameWithoutExtension(originalName);
        return $"{baseName}-compressed.{newExt}";
    }
}

Program.cs (DI 등록 + 옵션 라이센스)

using Aspose.Imaging;
using ImageApi.Services;

var builder = WebApplication.CreateBuilder(args);

// Optional: initialize Aspose license from a file or stream if you have one
// var license = new Aspose.Imaging.License();
// license.SetLicense("Aspose.Total.lic");

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IImageCompressionService, ImageCompressionService>();

var app = builder.Build();

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

// Enable for local testing
app.UseSwagger();
app.UseSwaggerUI();

app.Run();

단계별 가이드

단계 1 : 프로젝트 설정

ASP.NET Core Web API 프로젝트 만들기 Aspose.Imaging 첨부 파일 : 창조하기 Models, Services그리고, 그리고 Controllers 위에 나타난 것처럼 폴더.

단계 2 : Aspose.Imaging을 설정합니다 (선택 라이센스)

당신이 라이센스를 가지고 있다면, 스타트업에서 그것을 시작하십시오 (보기) Program.cs이것은 평가 물표를 피하고 완전한 기능을 보장합니다.

단계 3: 압축 서비스 구현

그들의 ImageCompressionService:

  • 사진을 통해 업로드 Image.Load(Stream)
  • 옵션으로 메타데이터를 스트리트
  • 보존된 측면 비율을 가진 옵션 리시스
  • 형식에 적합한 옵션으로 JPEG 또는 PNG로 저장

단계 4: API 컨트롤러를 구축

ImageController 전시회 POST /api/images/compress 파일 및 쿼리 매개 변수 :

  • format: jpeg 또는 png (이상적인 jpeg)
  • quality: 1 ~ 100 (JPEG 만; 기본 80)
  • maxWidth/maxHeight: downscaling에 대한 제한
  • stripMetadata· 가짜 true 작은 생산을 위해

5단계: API 테스트

모든 HTTP 클라이언트를 사용하여 multipart/form-data 단일 파일 필드 이름을 가진 요청 file, 플러스 선택 쿼리 매개 변수. 확인:

  • Response Content-Type 경기 형식
  • 반환된 파일 크기가 줄어든다.
  • 예상대로 작업을 재개합니다.

디자인 선택 & 최고의 관행

  • 포맷 명확한 설정: JPEG 사용 QualityPNG는 예측 가능한 생산을 위해 손실이 없습니다.
  • 다운스케일 전에 암호화: 리시닝은 먼저 픽셀을 줄입니다 (대규모 우승), 그 다음 암코딩은 더 짧은 바이트.
  • 수정 입력: 보관 콘텐츠 유형, 파일 크기, 쿼리 제한.
  • ** 스트리밍**: 전체 파일을 메모리에 반복적으로 읽지 마십시오; 스트림을 짧고 검색 가능하게 유지하십시오.
  • Caching: 당신이 결정적 인 입력에서 이름 / 콘텐츠를 추출하는 경우 변경되지 않은 반응을 표시; 그렇지 않으면 사용 사례에 캐시 헤드셋을 삽입합니다.
  • Security: 콘텐츠 유형을 확인하고 의심스러운 지불을 거부합니다.
  • ** 관찰 가능성**: 이전/다음 기록 크기 및 사용된 매개 변수; 이것은 기본 사항을 조정하는 데 도움이됩니다.
  • Throttling: 공개적으로 노출되면, 속도 제한 또는 악용을 방지하기 위해 auth를 요구합니다.

일반 확장 (다음에 다운로드)

  • WebP/AVIF 암호화는 더 작은 이미지를 위해 (새 옵션을 추가/contentType/ 파일 확장 BuildOptions).
  • PNG 튜닝 (필터링 / 압축 수준) 당신은 손실없는 자산을 필요로하는 경우.
    • 프레세트 프로필* 예를 들어 thumbnail, preview, hires 알려진 매개 변수에 대한 지침.
  • ETags 또는 콘텐츠 해싱은 캐시에서 동일한 응답을 제공합니다.
  • Async 배치 끝점은 한 번에 여러 파일을 압축합니다.

Troubleshooting

    • 큰 수입* : 증가 RequestSizeLimit 또는 흐름을 temp 저장합니다.
  • Wrong 색상: 안전한 색 공간은 기본으로 처리됩니다; 고급 사례는 명시적인 색형이 필요할 수 있습니다.
  • ** 크기 감소 없음 (PNG)**: PNG는 손실이 없으며, 더 강력한 바이트 저축을 위해 JPEG로 다시 이동하거나 전환할 수 있습니다.

요약

당신은 지금 생산 준비가 된 역동적 인 이미지 압축 API를 사용하여 Aspose.Imaging. 컨트롤러는 업로드 및 매개 변수를 처리합니다; 서비스는 안전한, 형식 조명 압력 및 옵션 리시닝을 적용하고, 그 다음 캐시 헤드와 함께 적절하게 타입 된 응답을 다시 스트리밍합니다. 여기에서 더 많은 형식을 추가 할 수 있습니다, 프레세트, 그리고 당신의 웹 스택에 맞게 케시 전략.

More in this category