Dalam era digital hari ini, pengendalian gambar yang efisien sangat penting untuk aplikasi web dan APIs. Salah satu aspek utama pengelolaan gambar adalah kompresi, yang membantu dalam mengurangi ukuran file tanpa secara signifikan mengganggu kualitas. panduan ini membimbing Anda melalui membangun API kompres foto yang dinamis menggunakan Aspose.Imaging untuk .NET. Pada akhirnya, Anda akan memiliki API Web ASP.NET Core yang berfungsi yang menerima gambar dan mengembalikan output yang dikompresi sesuai dengan parameter pertanyaan (format, kualitas, resing, dan banyak lagi).

Aspose.Imaging adalah perpustakaan yang kuat untuk bekerja dengan gambar dalam .NET. Ia mendukung banyak format dan menyediakan fitur manipulasi yang kukuh, termasuk kerugian (JPEG) dan aliran kerja tidak kalah (PNG). kami akan menggunakannya untuk membangun layanan kompresi yang efisien dan scalable.

Apa yang akan dibangun

    • titik akhir : POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Input: Multipart file (gambar), parameter pertanyaan pilihan untuk format/kualitas/resize
  • ** outputs**: aliran gambar yang dikompresi dengan benar Content-Type dan caching headers
  • Keselamatan: Validasi jenis konten, batasan ukuran, dan dekode/encode yang disimpan

Persyaratan

  • .NET 8 (atau .Net 6+)
  • Proyek ASP.NET Core Web API
  • Untuk NuGet: Aspose.Imaging
  • Optional: inisialisasi lisensi di aplikasi startup (jika Anda menggunakan bangunan berlesen)

Struktur proyek (minimum)

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

Contoh lengkap (Service + Controller)

Mengganti ruang nama pemegang tempat dengan area nama proyek Anda.

/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) mengontrol kompresi kerugian.
  • Default PNG biasanya bagus untuk versi pertama; jika Anda membutuhkan PNS yang lebih kecil, Anda dapat menambahkan tuning lanjutan kemudian.
  • TryStripMetadata adalah pendekatan yang paling efisien; APIs metadata bervariasi berdasarkan format sumber.

/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 (Pendaftaran DI + lisensi pilihan)

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

Panduan Langkah-Langkah

Langkah 1: Mengatur proyek

Mencipta proyek ASP.NET Core Web API. Aspose.Imaging Pemasangan NuGet. membuat Models, Services, dan Controllers seperti yang ditunjukkan di atas.

Langkah 2: Mengkonfigurasi Aspose.Imaging (lisensi pilihan)

Jika Anda memiliki lisensi, memulai di startup (lihat Program.csIni menghindari penilaian indikator air dan memastikan fungsi penuh.

Langkah 3: Melaksanakan Layanan Kompresi

dan yang ImageCompressionService:

  • Mengunggah gambar melalui Image.Load(Stream)
  • Memilih Metadata Optional
  • Optional resizes dengan aspect ratio disimpan
  • Menyimpan ke JPEG atau PNG dengan opsi yang sesuai dengan format

Langkah 4: Membangun API Controller

ImageController Eksposur POST /api/images/compress Mengambil file dan query parameter:

  • format: jpeg atau png (dengan default jpeg)
  • quality1 – 100 (hanya JPEG; default 80)
  • maxWidth/maxHeightBatas untuk downscaling
  • stripMetadatadan default true Untuk produksi yang lebih kecil

Langkah 5: Menguji API

Gunakan klien HTTP untuk mengirimkan multipart/form-data Permintaan dengan satu file field yang disebut file, plus parameter pertanyaan pilihan. periksa:

  • Response Content-Type Format Pertandingan
  • Ukuran file yang dikembalikan dikurangi
  • Mengembalikan kerja seperti yang diharapkan

Pilihan Desain & Praktik Terbaik

  • Pengaturan format-aware*: menggunakan JPEG QualityPNG tetap kekurangan untuk hasil yang dapat diramalkan.
  • Downscale sebelum coding: Resizing mengurangi piksel terlebih dahulu (pemenang ukuran terbesar), kemudian menyulitkan byte lebih lanjut.
  • Sanitize input: jenis konten yang disimpan, ukuran file, batas pertanyaan.
  • Streaming: Hindari membaca seluruh file ke dalam memori berulang kali; menjaga aliran jangka pendek dan dapat dicari.
  • Caching: Tanda respons sebagai tidak berubah jika Anda mengalir nama / konten dari input deterministik; jika tidak, tun headers cache ke kasus penggunaan Anda.
  • Keselamatan: Validasi jenis konten dan menolak beban yang mencurigakan. pertimbangkan pemindaian untuk gambar yang rosak.
  • Pengamatan: Ukuran log sebelum/selepas dan parameter yang digunakan; ini membantu Anda mengatur default.
  • Trotling: Jika didedikasikan secara publik, batas tarif atau memerlukan auth untuk mencegah penyalahgunaan.

Perpanjangan umum (drop-in kemudian)

  • Kode WebP/AVIF* untuk gambar yang lebih kecil (tambah opsi baru/contentType/ file extension dalam BuildOptions).
  • PNG tuning (tingkat penapis/kompresi) jika Anda membutuhkan aset ekstra kecil tanpa kerugian.
  • Predet profil seperti thumbnail, preview, hires Menghitung parameter yang diketahui.
  • Etag atau hashing konten untuk melayani respons yang sama dari cache.
  • Async batch endpoint untuk mengkompresi beberapa file sekaligus.

Troubleshooting

  • Pengeluaran yang besar: Meningkat RequestSizeLimit atau aliran untuk temp penyimpanan.
  • Wrong warna: Jaminan ruang warna diperlakukan oleh default; kasus lanjutan mungkin memerlukan jenis warna yang jelas.
  • Tidak ada pengurangan ukuran (PNG): PNG tidak kalah; memungkinkan resize atau switch ke JPEG untuk simpanan byte yang lebih kuat.

Kesimpulannya

Anda sekarang memiliki API kompresi gambar dinamik yang siap produksi menggunakan Aspose.Imaging. Controller mengendalikan upload dan parameter; layanan memohon kompres yang aman, format-informasi dan resing optional, kemudian mengalir kembali respons yang dicetak dengan headers cache. Dari sini, Anda dapat menambahkan lebih banyak format, preset, dan strategi caching untuk menyesuaikan stack web Anda.

More in this category