W dzisiejszej epoce cyfrowej efektywna obsługa obrazu jest kluczowa dla aplikacji internetowych i API. Jednym z głównych aspektów zarządzania obrazem jest kompresja, która pomaga w zmniejszaniu rozmiarów plików bez znaczącego zakłócania jakości. Ten przewodnik prowadzi Cię poprzez budowę dynamicznej API kompresii obrazów za pomocą Aspose.Imaging dla .NET. Do końca, będziesz miał funkcjonalny ASP.NET Core Web API, który akceptuje obrazy i zwraca komprimowane wyniki zgodnie z parametrami zapytania (format, jakość, resing i wiele innych).
Aspose.Imaging to potężna biblioteka do pracy z obrazami w .NET. obsługuje wiele formatów i zapewnia solidne funkcje manipulacji, w tym straty (JPEG) i bez strat (PNG) przepływów roboczych.
Co będziecie budować
- • punkt końcowy:
POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
- Wpisy: plik wielostronny (zdjęcie), opcjonalne parametry zapytania dla formatu / jakości / rozmiaru
- Outputs: strumień kompresyjny obrazu z poprawnym
Content-Type
i caching headers - Bezpieczeństwo: weryfikacja typu zawartości, limity rozmiaru oraz zapisywany kod / kod
Warunki
- Źródło: .NET 6+
- Projekt ASP.NET Core Web API
- W Nowym Jorku:
Aspose.Imaging
- Opcjonalne: inicjalizacja licencji w aplikacji startup (jeśli używasz licencjonowanego budynku)
Struktura projektu (minimalna)
/Controllers
ImageController.cs
/Services
ImageCompressionService.cs
/Models
CompressionRequest.cs
Program.cs
appsettings.json
Pełny przykład (Service + Controller)
Zastąpić przestrzeń nazwiskową właściciela lokalu nazwą przestrzeni projektowej.
/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) kontroluje kompresję stratową.- Defaulty PNG są zwykle dobre dla pierwszej wersji; jeśli potrzebujesz dodatkowych małych pNG, możesz dodać zaawansowane tuning później.
TryStripMetadata
jest najlepszym podejściem; metadata APIs różnią się w zależności od formatu źródłowego.
/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
(Zarejestruj się + licencja opcjonalna)
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();
Przewodnik krok po kroku
Krok 1: Zorganizuj projekt
Tworzenie projektu ASP.NET Core Web API. Aspose.Imaging
Pakiet NuGet. utworzyć Models
, Services
, i Controllers
folderów, jak pokazano powyżej.
Krok 2: Konfiguruj Aspose.Imaging (opcjonalna licencja)
Jeśli masz licencję, inicjalizuj ją na starcie (patrz Program.cs
To unika oceny znaków wodnych i zapewnia pełną funkcjonalność.
Krok 3: Wdrożenie usługi kompresji
W tym ImageCompressionService
:
- Pobierz obrazy poprzez
Image.Load(Stream)
- Opcjonalnie wyciąga metadane
- Opcjonalnie rezygnuje z zachowanym stosunkiem aspektowym
- Oszczędności do JPEG lub PNG z opcjami odpowiadającymi formatowi
Krok 4: Stwórz kontroler API
ImageController
Ekspozycje POST /api/images/compress
Przyjmowanie pliku i zapytania parametrów:
format
:jpeg
lubpng
(podstawowejpeg
)quality
1–100 (tylko JPEG; standard 80)maxWidth
/maxHeight
• ograniczenia do spadkustripMetadata
• Defaulttrue
Dla mniejszej produkcji
Krok 5: Przegląd API
Użyj dowolnego klienta HTTP, aby wysłać multipart/form-data
żądanie z jednym polem pliku o nazwie file
, plus opcjonalne parametry zapytania. sprawdź:
- Response
Content-Type
Formaty meczów - Rozmiar pliku zwróconego jest zmniejszony
- Rekonstrukcja prac zgodnie z oczekiwaniami
Opcje projektowe i najlepsze praktyki
- Ustawienia format-aware: używa JPEG
Quality
PNG pozostaje bez strat na przewidywalny wydajność. - Downscale przed kodowaniem: Resizing zmniejsza piksele najpierw (największe rozmiary wygrywają), a następnie kodowanie skrótów byty dalej.
- Sanityzuj wpisy: typ treści, rozmiar pliku, granice zapytania.
- ** Streaming**: Unikaj czytania całego pliku w pamięci wielokrotnie; utrzymanie strumieni krótkoterminowych i wyszukiwalnych.
- Caching: Zaznacz odpowiedź jako niezmienna, jeśli wyodrębnisz nazwę / zawartość z wpisów deterministycznych; w przeciwnym razie naciśnij nagłówki cache do swojego przypadku użycia.
- Bezpieczeństwo: weryfikuj typ treści i odrzuć podejrzane obciążenia.
- Obserwowalność: Rozmiary rejestru przed/po i używane parametry; to pomaga ustalić wady.
- Przestrzeganie: Jeśli wystawione publicznie, limit stopy lub wymaga auth w celu zapobiegania nadużyciom.
Wspólne rozszerzenia (drop-in później)
- Kodery WebP/AVIF dla jeszcze mniejszych obrazów (dodaj nowe opcje/
contentType
Rozszerzenie pliku wBuildOptions
). - PNG tuning (poziom filtracji/kompresji) jeśli potrzebujesz dodatkowo małych aktywów bez strat.
- Predstawianie profilów jak
thumbnail
,preview
,hires
Wykorzystywanie znanych parametrów. - Tags lub hashing zawartości, aby służyć identycznym odpowiedziom z cache.
- Async batch końcowy punkt do kompresji wielu plików naraz.
Troubleshooting
- Wielkie wkłady *: Wzrost
RequestSizeLimit
lub przepływ do temp przechowywania.
- Wielkie wkłady *: Wzrost
- Czerwone kolory: Zabezpieczony przestrzeń kolorów jest obsługiwany przez domyślne; zaawansowane przypadki mogą wymagać wyraźnego typu koloru.
- ** Brak redukcji wielkości (PNG)**: PNG jest bez strat; umożliwia ponowne rozmieszczenie lub przejście do JPEG dla silniejszych oszczędności bytu.
Podsumowanie
Teraz masz gotowy do produkcji dynamiczny kompresji obrazu API za pomocą Aspose.Imaging. sterownik obsługuje przesyłki i parametry; usługa stosuje bezpieczne, format-aware kompresię i opcjonalne odtwarzanie, a następnie przenosi odpowiednio typowane odpowiedzi z nagłówkami cache. Odtąd można dodać więcej formatów, presetów i strategii caching, aby dopasować się do twojego web stack.