في العصر الرقمي الحالي ، فإن إدارة الصورة الفعالة أمر بالغ الأهمية لتطبيقات الويب و APIs. واحدة من الجوانب الرئيسية لإدارة الصور هي الضغط ، مما يساعد في تقليل حجم الملفات دون إلحاق الضرر الكبير بالجودة. هذا الدليل يسير لك من خلال بناء API ضغط الصور الديناميكية باستخدام Aspose.Imaging ل .NET. في نهاية المطاف ، سيكون لديك API ASP.NET Core Web الوظيفي الذي يقبل الصور ويعيد الناتج المضغوط وفقًا لمعايير الاستعلام (التنسيق ، الجودة ، إعادة التدوير ، وما إلى ذلك).
Aspose.Imaging هي مكتبة قوية للعمل مع الصور في .NET. يدعم العديد من التنسيقات وتوفر ميزات التلاعب الصلبة، بما في ذلك تدفقات العمل الخسارة (JPEG) و الخاسرة (PNG).
ماذا ستبني
- نقطة النهاية :
POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
- نقطة النهاية :
- الإدخالات: ملف متعدد الأجزاء (الصورة) ، معلمات الاستعلام الاختيارية للتنسيق / الجودة / الحجم
- الخروج: تدفق الصورة المضغوط مع الصحيح
Content-Type
كاتشينغ الرؤوس - السلامة: التحقق من نوع المحتوى، الحد الأقصى للأحجام، والتخزين/الرمز المحفوظ
المتطلبات
- نيت 8 (أو .NET 6+)
- برنامج ASP.NET Core Web 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 جيدة للنسخة الأولى ؛ إذا كنت بحاجة إلى أجزاء صغيرة إضافية ، فيمكنك إضافة التركيب المتقدم في وقت لاحق.
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
(التسجيل + الترخيص الاختياري)
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();
دليل خطوة بخطوة
الخطوة الأولى: إعداد المشروع
إنشاء مشروع 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
الحد الأدنى للانخفاضstripMetadata
• الافتراضtrue
من أجل إنتاج أقل
الخطوة الخامسة: اختبار النار
استخدم أي عميل HTTP لإرسال multipart/form-data
طلب مع حقل ملف واحد يسمى file
، بالإضافة إلى معلمات الاستعلام الاختيارية.تحقق من:
- Response
Content-Type
مباريات تنسيق - يتم تقليل حجم الملفات التي تم إرجاعها
- إعادة العمل كما هو متوقع
خيارات التصميم وأفضل الممارسات
- إعدادات الإشارة إلى النموذج: يستخدم JPEG
Quality
PNG يبقى خالية من الخسائر لإنتاج قابل للتنبؤ. - Downscale before encoding: يقلل إعادة التدوير من البكسل أولاً (أكبر حجم يفوز) ، ثم يتدفق الترميز إلى بايتات أخرى.
- إدخالات الصرف الصحي: نوع المحتوى المحفوظ، حجم الملف، حدود الاستعلام.
- تدفق: تجنب قراءة الملف بأكمله إلى الذاكرة مرارا وتكرارا؛ والحفاظ على التدفقات قصيرة الأمد ويمكن البحث عنها.
- Caching: علامة ردود الفعل غير قابلة للتغيير إذا تم استنتاج الاسم/المحتوى من الإدخالات المحددة؛ وإلا قم بتحويل عناوين التخزين إلى حالة الاستخدام الخاصة بك.
- الأمن: تأكيد نوع المحتوى ورفض التحميلات المشبوهة.
- ملاحظة: حجم السجل قبل/بعد والمعايير المستخدمة، وهذا يساعدك على تحديد الافتراضات.
- الخيانة: إذا تم التعرض لها علنا، الحد الأدنى للمعدل أو المطلوب من Auth لمنع الإساءة.
التمديدات الشائعة (إلغاء في وقت لاحق)
- تشفير WebP/AVIF للصور الصغيرة (إضافة خيارات جديدة/
contentType
/ ملف التمديد فيBuildOptions
). - PNG tuning (مستوى الترشيح / الضغط) إذا كنت بحاجة إلى أصول صغيرة إضافية خالية من الخسائر.
- إعداد ملفات تعريفية * مثل
thumbnail
,preview
,hires
التخطيط إلى المعلمات المعروفة.
- إعداد ملفات تعريفية * مثل
- ** إيتاغ** أو hashing المحتوى لخدمة إجابات مماثلة من التخزين.
- Async batch نقطة النهاية لضغط العديد من الملفات في وقت واحد.
Troubleshooting
- الإيرادات الكبيرة: زيادة
RequestSizeLimit
أو تدفق إلى temp التخزين. - الألوان الخاطئة: يتم التعامل مع المساحة الملونة المضمونة عن طريق الافتراضات؛ قد تحتاج الحالات المتقدمة إلى نوع ملون صريح.
- لا تخفيض حجم (PNG): PNG خالية من الخسائر؛ يتيح إعادة التحويل أو التبديل إلى JPEG للحصول على توفير بايت أقوى.
ملخص
لديك الآن إنتاج جاهز ديناميكية ضغط الصورة API باستخدام Aspose.Imaging.السيطرة تتعامل مع التحميلات والمعايير؛ الخدمة تطبق الضغط آمنة، تنسيق واضحة وإعادة التدوير الاختياري، ثم تدفق مرة أخرى استجابة مكتوبة بشكل صحيح مع عناوين التخزين.من هنا، يمكنك إضافة المزيد من التنسيقات، المجلدات، واستراتيجيات التشفير لتناسب موقع الويب الخاص بك.