Image Upload & Gallery System(Back End)

ASP.NET Core Backend — সম্পূর্ণ ব্যাখ্যা (Zero থেকে)

আগে দেখো এই backend এ 2টা class আছে। এরা কীভাবে একসাথে কাজ করে:

Angular (Frontend)
      │
      │  HTTP Request
      ▼
ImagesController        ← দরজা (Request receive করে)
      │
      │  কাজ delegate করে
      ▼
ImageRepository         ← কারখানা (আসল কাজ করে)
      │
      ├──▶ File System (image save করে wwwroot/images তে)
      │
      └──▶ Database (image info save করে)

Analogy: Controller হলো হোটেলের রিসেপশন — guest কে receive করে, কাজ অন্যকে দেয়। Repository হলো হোটেলের ম্যানেজার — আসল কাজ সে করে।


🏗️ PART 1 — ImageRepository (কারখানা)

Step 1.1 — Constructor & Dependency Injection

public class ImageRepository
{
    private readonly BlogDbContext dbContext;
    private readonly IWebHostEnvironment env;
    private readonly IHttpContextAccessor httpContextAccessor;

    public ImageRepository(BlogDbContext dbContext,
                           IWebHostEnvironment env,
                           IHttpContextAccessor httpContextAccessor)
    {
        this.dbContext = dbContext;
        this.env = env;
        this.httpContextAccessor = httpContextAccessor;
    }
}

3টা জিনিস inject হচ্ছে:

Dependencyকী করেReal Example
BlogDbContextDatabase এর সাথে কথা বলেdbContext.BlogImages.AddAsync()
IWebHostEnvironmentServer এর folder path জানেenv.WebRootPath"C:/project/wwwroot"
IHttpContextAccessorCurrent HTTP request এর info দেয়Scheme, Host জানার জন্য

Dependency Injection কী?

// ❌ পুরনো way — নিজে object বানানো (tight coupling)
var env = new WebHostEnvironment();   // এভাবে করা যায় না, ভুল

// ✅ DI way — Angular এর মতোই, ASP.NET নিজে inject করে
public ImageRepository(IWebHostEnvironment env)  // ASP.NET বলছে "আমি দেবো"

Analogy: তুমি রান্না করবে, কিন্তু বাজার থেকে মাল আনবে না — কেউ এনে দেবে। DI হলো সেই মাল এনে দেওয়ার system


Step 1.2 — UploadAsync() Method — ধাপে ধাপে

public async Task<BlogImage> UploadAsync(ImageUploadRequestDto request)

async Task<BlogImage> মানে — এই method async (অপেক্ষা করতে পারবে) এবং শেষে একটা BlogImage object return করবে।


🔹 ধাপ 1 — File Extension বের করা

var extension = Path.GetExtension(request.File.FileName);
var fileName = $"{Guid.NewGuid()}{extension}";

Example:

request.File.FileName = "my-photo.jpg"

Path.GetExtension("my-photo.jpg") = ".jpg"

Guid.NewGuid() = "a3f8c2d1-4b5e-..."  (random unique ID)

fileName = "a3f8c2d1-4b5e-....jpg"

কেন Guid? User যদি দুইজন একই নামের file upload করে — conflict হবে। Guid দিয়ে প্রতিটা file কে unique name দেওয়া হচ্ছে।

Analogy: দুইজনের নাম “Navid” হতে পারে, কিন্তু Student ID (22-46956-1) সবসময় unique — Guid হলো সেই Student ID।


🔹 ধাপ 2 — File Save করার Path বানানো

var localPath = Path.Combine(env.WebRootPath, "images", fileName);

Example:

env.WebRootPath = "C:/MyProject/wwwroot"
fileName       = "a3f8c2d1.jpg"

localPath = "C:/MyProject/wwwroot/images/a3f8c2d1.jpg"

Path.Combine() ব্যবহার করা হয় কারণ Windows এ \ আর Linux এ /Path.Combine সেটা নিজেই handle করে।


🔹 ধাপ 3 — Folder তৈরি করা (না থাকলে)

if (!Directory.Exists(Path.Combine(env.WebRootPath, "images")))
{
    Directory.CreateDirectory(Path.Combine(env.WebRootPath, "images"));
}

কেন? প্রথমবার upload এর সময় wwwroot/images folder নাও থাকতে পারে। না থাকলে file save করতে গেলে error হবে। তাই আগে folder আছে কিনা check করো, না থাকলে বানাও।

wwwroot/
├── css/
├── js/
└── images/    ← এই folder না থাকলে এখানে বানানো হচ্ছে

🔹 ধাপ 4 — File Disk এ Save করা

using (var stream = new FileStream(localPath, FileMode.Create))
{
    await request.File.CopyToAsync(stream);
}

এটা কীভাবে কাজ করে?

request.File (memory তে আছে, Angular থেকে আসা)
      │
      │  CopyToAsync()
      ▼
FileStream (disk এ write করার channel)
      │
      ▼
"C:/MyProject/wwwroot/images/a3f8c2d1.jpg"  ← disk এ save হলো

using block কেন? File stream use শেষে automatically close হয়ে যাবে — memory leak হবে না।

Analogy: পানির পাইপ খুললে কাজ শেষে বন্ধ করতে হয়। using হলো সেই automatic পাইপ বন্ধ করার system।


🔹 ধাপ 5 — Public URL বানানো

var httpRequest = httpContextAccessor.HttpContext.Request;
var url = $"{httpRequest.Scheme}://{httpRequest.Host}/images/{fileName}";

Example:

httpRequest.Scheme = "https"
httpRequest.Host   = "localhost:5001"
fileName           = "a3f8c2d1.jpg"

url = "https://localhost:5001/images/a3f8c2d1.jpg"

কেন HttpContextAccessor দিয়ে? Development এ localhost:5001, production এ myblog.com — hardcode না করে runtime এ dynamically বানানো হচ্ছে।

এই URL টাই Angular এ দেখাবে <img [src]="..."> তে।


🔹 ধাপ 6 — Database এ Save করা

var blogImage = new BlogImage
{
    Id = Guid.NewGuid(),
    FileName = fileName,              // "a3f8c2d1.jpg"
    Title = request.Title,            // "My Banner"
    FileExtension = extension,        // ".jpg"
    FileSizeInBytes = request.File.Length,  // 204800 (bytes)
    Url = url                         // "https://localhost:5001/images/a3f8c2d1.jpg"
};

await dbContext.BlogImages.AddAsync(blogImage);
await dbContext.SaveChangesAsync();

return blogImage;

Database এ কী save হচ্ছে:

BlogImages Table:
┌──────────────────┬───────────────┬───────────────┬──────────────────────────────────────┐
│ Id               │ FileName      │ Title         │ Url                                  │
├──────────────────┼───────────────┼───────────────┼──────────────────────────────────────┤
│ b2c3-d4e5-...    │ a3f8c2d1.jpg  │ My Banner     │ https://localhost:5001/images/a3f8.. │
└──────────────────┴───────────────┴───────────────┴──────────────────────────────────────┘

File system এ কী save হচ্ছে:

wwwroot/images/a3f8c2d1.jpg  ← actual image file

⚠️ Important: Database এ actual image নেই — শুধু URL আছে। Image আছে disk এ। DB শুধু “ঠিকানা” রাখে।


Step 1.3 — GetAllAsync() Method

public async Task<List<BlogImage>> GetAllAsync()
{
    return await dbContext.BlogImages.ToListAsync();
}

এটা simple — Database এর BlogImages table এর সব row নিয়ে List বানিয়ে return করে।

Database → সব BlogImage rows → List<BlogImage> → Controller → Angular

🚪 PART 2 — ImagesController (দরজা)

Step 2.1 — Controller Setup

[Route("api/[controller]")]   // → "api/images"
[ApiController]
public class ImagesController : ControllerBase
{
    private readonly ImageRepository imageRepository;

    public ImagesController(ImageRepository imageRepository)
    {
        this.imageRepository = imageRepository;  // DI দিয়ে repository inject
    }
}

[Route("api/[controller]")][controller] মানে class name থেকে “Controller” বাদ দিলে যা থাকে = "images"। তাই full route হয় api/images


Step 2.2 — Upload Endpoint

[HttpPost("upload")]   // → POST api/images/upload
public async Task<IActionResult> Upload([FromForm] ImageUploadRequestDto request)
{
    if (request.File == null || request.File.Length == 0)
        return BadRequest("No file");       // 400 error

    var image = await imageRepository.UploadAsync(request);
    return Ok(image.Url);                   // 200 + URL string
}

[FromForm] কেন? Angular থেকে FormData আসছে (multipart/form-data)। [FromForm] বলছে — “JSON না, form data থেকে read করো।”

Response:

✅ Success → HTTP 200 → "https://localhost:5001/images/a3f8c2d1.jpg"
❌ No file → HTTP 400 → "No file"

Step 2.3 — Gallery Endpoint

[HttpGet]             // → GET api/images
public async Task<IActionResult> GetAll()
{
    var images = await imageRepository.GetAllAsync();
    return Ok(images);
}

Response:

HTTP 200 →
[
  { "id": "b2c3...", "url": "https://...jpg", "title": "Banner", ... },
  { "id": "d4e5...", "url": "https://...png", "title": "Photo",  ... }
]

🔄 Complete End-to-End Flow

Upload Flow:

Angular: imageService.upload(file, name, title)
    │  POST api/images/upload (FormData)
    ▼
ImagesController.Upload()
    │  validation check
    ▼
ImageRepository.UploadAsync()
    │
    ├── Guid দিয়ে unique fileName বানাও
    ├── wwwroot/images/ folder check/create
    ├── FileStream দিয়ে disk এ save করো
    ├── URL বানাও (scheme + host + path)
    └── DB তে BlogImage save করো
    │
    ▼
Controller → return Ok(image.Url)
    │  "https://localhost:5001/images/a3f8c2d1.jpg"
    ▼
Angular: imageSelected.emit(url) → Parent এ URL পাঠায়

Gallery Load Flow:

Angular: imageService.getAll()
    │  GET api/images
    ▼
ImagesController.GetAll()
    │
    ▼
ImageRepository.GetAllAsync()
    │  dbContext.BlogImages.ToListAsync()
    ▼
Database → সব rows
    │
    ▼
Controller → return Ok(images)   [JSON array]
    │
    ▼
Angular: images.set(res) → Signal update → Gallery render

🧠 Key Concepts Summary

Conceptকী করেExample
Repository PatternDB logic আলাদা রাখেImageRepository
Dependency InjectionObject ASP.NET নিজে দেয়Constructor এ inject
[FromForm]FormData parse করেFile upload এর জন্য
Guid.NewGuid()Unique ID/filename বানায়Collision এড়াতে
IWebHostEnvironmentServer path জানেwwwroot path পেতে
FileStreamDisk এ file write করেImage save করতে
IHttpContextAccessorRuntime এ URL বানায়Hardcode এড়াতে
async/awaitBlock না করে অপেক্ষা করেDB/File I/O তে

এই pattern টা বুঝলে যেকোনো ASP.NET Core project এ file upload system বানাতে পারবে!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top