How to build and deploy Image Hosting Service on Seola

by SkillAiNest

When most people think about hosting the icon, they imagine uploading photos to the cloud service and returning a simple link.

It feels smooth, but behind this experience a powerful set of technologies is sitting. Basically there is something called object storage, which is a different way to handle files than a traditional database or file system.

In this article, we will create a full photo hosting service using node.js And express, connect it to Object Storage, and finally, deploy the entire project Civic.

Finally, you will have a working application that allows users to upload images and recover through the host URL, all walking on the cloud.

The table of content

What is Object Storage?

To understand that the way our project is designed, we need to understand the first object storage.

Traditional file storage systems save files in your computer’s file explorer rating folders. Block storage systems, which are often used in the database, divide the data into pieces and manage speed and reliability.

Object storage is different. It treats each file, whether a photo, video, or document as a single item. Each item is stored with a unique identifier inside its metadata and a flat structure, commonly known as bucket.

This flat architecture extends object storage without any extent. Instead of worrying about file routes or directors, you easily put something in the bucket and get back to the identifier.

Amazon S3 What is the standard of the industry for the object storage, offers mass scale, global duplication, and modern features, but it comes with additional complexity and often unexpected costs. On the other hand, Seola’s Object Storage is designed for those developers who want the same stability and scalebuability without steep learning curves.

It provides an easy setup, and is compatible with the S3, so communicate with it is the same as use the S3 bucket without extra setup and complexity. Although S3 is ideal for data bytes businesses, Seola solution is best for projects like image hosting, blogs, or mobile apps where ease and speed are the most important.

What will we build

We will create a simple but practical image hosting service. In the core part, the service allows the user to send a picture through the HTTP application. The server will accept this photo, take action and store it in object storage.

The utility of such a project is far from coding exercises. If you are creating a blog, you can use this service to store photos for your posts without worrying about file management on your web server.

If you are developing a mobile app that requires profile pictures or photo sharing, this backdrop may work as your basis. Even if you just want to understand how the cloud-local applications handle the file upload, this project gives you a clear, hand-experience.

Finally, you won’t just have a code locally. We will deploy the application to Seola, which means that your image hosting service will be directly accessible to everyone with extension and link.

How to configure the project

Let’s start by setting up a node Dot JS project. You can Clone this reservoir If you do not want to set the project from the beginning.

Make a new project directory, start it with NPM, and install the desired dependence.

npm init -y
npm i express multer dotenv @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

We will use Express For our web server, Moderator To handle the file uploads, and AWS SDK To be connected with object storage. The molder works as the middleware, which gives us easy access to the uploaded files. AWS SDK provides us with programming access to object storage, which allows us to upload files and create links.

Let’s write a hurry index.html And keep it in public/ Directory to serve as UI for uploading file.

html>
<html lang="en">
<head>
  <meta charset="utf-8" /> 
  <meta name="viewport" content="width=device-width,initial-scale=1" /> 
  <title>Pic Hosttitle>

  
  <style>
    :root 
        originalname: req.file.originalname  
    body  "",
      
    h1  "",
      
    form, .card  "",
      
    input(type="file") { margin: .5rem 0 1rem; }
    button { 
      padding: .6rem 1rem; 
      border-radius: 10px; 
      border: 1px solid #9995; 
      background: #0000FF; 
      cursor: pointer; 
    }
    #result { margin-top: 1rem; display: none; }
    #result a { word-break: break-all; } 
  style>
head>
<body>
  
  <h1>Simple Image Hosth1>

  
  <form id="uploadForm" class="card">
    <label for="file">Choose imagelabel><br/>
    <input id="file" name="file" type="file" accept="image/*" required />
    <br/>
    <button type="submit">Uploadbutton>
    
    <div id="status" aria-live="polite" style="margin-top:.75rem;">div>
  form>

  
  <div id="result" class="card">
    <div>
      <strong>Share this page:strong> 
      <a id="pageUrl" href="#" target="_blank" rel="noopener">a>
    div>
  div>

  
  <script>
    const form = document.getElementById('uploadForm');   
    const statusEl = document.getElementById('status');   
    const result = document.getElementById('result');     
    const pageUrlEl = document.getElementById('pageUrl'); 
    const directUrlEl = document.getElementById('directUrl'); 

    
    form.addEventListener('submit', async (e) => {
      e.preventDefault(); 
      statusEl.textContent = 'Uploading...'; 
      result.style.display = 'none';

      const fd = new FormData(); 
      const file = document.getElementById('file').files(0);
      if (!file) {
        statusEl.textContent = 'Pick a file first.';
        return;
      }
      fd.append('file', file); 

      try {
        
        const res = await fetch('/upload', { method: 'POST', body: fd });
        if (!res.ok) throw new Error('Upload failed');
        const data = await res.json();

        
        pageUrlEl.textContent = data.pageUrl;
        pageUrlEl.href = data.pageUrl;

        
        result.style.display = 'block';
        statusEl.textContent = 'Done!';
        form.reset();
      } catch (err) {
        
        statusEl.textContent = 'Error: ' + err.message;
      }
    });
  script>
body>
html>

When the user goes on the page, they will see an easy upload form with the file selector. They can select a picture from their computer and click upload. Then stops submission of form using JavaScript addEventListener('submit')Prevents the browser from refreshing the full page, and instead, packs the selected file in one FormData Objection

This file is then sent to the server with one fetch Call /upload Path if the server responds successfully, JSON returned A pageUrl. This URL result appears inside the card, which was initially hidden. The user can now copy this link and share it with others.

If something has gone wrong, such as the file is not being selected, the server is being mistaken, or if there is a failure to upload, the script updates the status message to inform the user.

How does this user feel?

Index.html

Let’s make a backdoor using now server.js File

import path from "path"; 
import express from "express"; 
import multer from "multer"; 
import crypto from "crypto"; 
import dotenv from "dotenv"; 
import { fileURLToPath } from "url"; 
import {
  S3Client,
  PutObjectCommand,
  HeadObjectCommand,
  GetObjectCommand,
} from "@aws-sdk/client-s3"; 
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; 

dotenv.config(); 


const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);


const S3_BUCKET = process.env.S3_BUCKET;


const s3 = new S3Client({
  region: "auto", 
  endpoint: process.env.ENDPOINT, 
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID, 
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, 
  },
});


const app = express();


app.use(express.static(path.join(__dirname, "public")));



const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 },
});



app.get("/", (req, res) => {
  res.sendFile(path.join(__dirname, "public", "index.html"));
});



app.post("/upload", upload.single("file"), async (req, res) => {
  try {
    
    if (!req.file) return res.status(400).json({ error: "file is required" });

    
    const id = crypto.randomUUID().replace(/-/g, "");
    const key = id;

    
    const put = new PutObjectCommand({
      Bucket: S3_BUCKET,
      Key: key,
      Body: req.file.buffer,
      ContentType: req.file.mimetype,
      Metadata: {
        originalname: req.file.originalname || "",
      },
    });

    
    await s3.send(put);

    
    const baseUrl = `${req.protocol}://${req.get("host")}`;
    const pageUrl = `${baseUrl}/i/${id}`;

    
    res.json({ id, pageUrl });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: "upload_failed" });
  }
});



app.get("/i/:id", async (req, res) => {
  const { id } = req.params;
  const key = id;

  try {
    
    await s3.send(new HeadObjectCommand({ Bucket: S3_BUCKET, Key: key }));

    
    const command = new GetObjectCommand({ Bucket: S3_BUCKET, Key: key });
    const signedUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });

    
    return res.redirect(302, signedUrl);
  } catch (err) {
    console.error(err);
    return res.status(404).send("Not found");
  }
});


app.listen(process.env.PORT || 3000, () => {
  console.log(`Image host server listening for requests...`);
});

Root 1: GET /

This is the app’s entry point. When you open the browser and go to the Root URL, it serves it index.html File from the file public The folder has an upload form in the file where the user can select and submit it.

Route 2: POST /upload

This is the place where magic is. When the user chooses an image and clicks on “upload”, the file is sent to this closing point. The maler handle the file in memory, and then the file is pushed to the storage object using PutObjectCommand. A random unique ID is created as a file key. Once uploaded, the server responds with A pageUrl It can be used later to see the uploaded image.

Route 3: GET /i/:id

This route retrieves an uploaded photo. Instead of presenting the file directly, it produces a signed URL using an hour getSignedUrl. This signed URL provides temporary access to the file stored in Object Storage. The server then sends the user to the signed URL. If the file is not available, it returns 404 error.

Before running this code, we need access to object storage and add the environmental file price. You are seeing the code process.env Recover these values ​​and helps us verify with object storage to read and write files.

How to make your Object Storage

Log Click Seola and “Object Storage”. Click on “Make Object Storage” and give it a name.

Creating Object Storage

Once you become, click “Settings” and you will see the key and secret key. We need these four values

  • The name of the bucket

  • End Point URL

  • Access the key

  • Secret key

Object storage access keys

Copy them to the designated file .env Inside your project

AWS_ACCESS_KEY_ID=YOUR_ACCESS_KEY_ID_HERE
AWS_SECRET_ACCESS_KEY=YOUR_SECRET_ACCESS_KEY_HERE
S3_BUCKET=YOUR_BUCKET_NAME_HERE
ENDPOINT=YOUR_ENDPOINT_URL_HERE

In addition, make public access to the settings so that you can advance files from your local environment.

Public access enabled

Checking the application locally

Let’s make sure our code works locally.

node server.js

Barley And try to upload the file. It should be given to the URL after seeing the file after a successful upload.

Upload the file

You can visit the URL to view your uploaded file. You can also check whether it has been uploaded using the Object Storage UI.

Object storage ui

Great We have created a simple image hosting and sharing service. Now enter the cloud.

How to deploy your project to Seola

First, press your project to Gut Hub or Fork my repository. Then log in to your seola dashboard and create a new application.

Make a request

Connect your Gut Hub account, select the storage that has your image hosting service, and select the branch you want to deploy. Ciula will automatically detect this node. There is a JS project and install dependence. It will also apply to a specific port.

To create AWS credentials and bucket information, go to the Environmental variable section in your app and add your AWS_ACCESS_KEY_IDFor, for, for,. AWS_SECRET_ACCESS_KEYFor, for, for,. AWS_REGIONAnd S3_BUCKET_NAME. These values ​​will be injected into your application at the time of run -time, making sure that sensitive data is not a strict code in your source code.

Adding environmental variables

After adding environmental variables, go to the “review” and click “deploy”.

FBCFCD74-D74E-43AD-9B99-7F02421CF5DF

Wait for a few minutes. Once deployed. After completion, Seola will give you URL directly. Click “View the app” to go to your application page.

Straight URL

Congratulations! Your app is now alive. You can share the URL with others or add customs domain to your own app to keep your picture hosting solutions in your picture.

Why does this project make a difference

This project is more than just one coding exercise. It teaches you how modern applications manage files, introduce you to object to storage, and tell you how to integrate cloud services into their plans.

Along with Seola, you also learned how to deploy the production applications for production, provide you with a full -fledged cycle from local prototype to the cloud service.

Blogs, mobile apps, or even internal tools construction for developers, images, images are reliable and the ability to host the scale. Object storage and a simple node. With the JS service, you can avoid re -connecting the wheel and rely on cloud infrastructure.

Conclusion

We started through the detection of object storage and why it is ideal to handle files like photos. We then created a node Dot JS application that accepts uploads, stores them in Seola object storage, and returns accessible to the URL. Finally, we deployed the application on Seola, turned the local project directly into an image hosting service. On the way, you not only got a working code but also got a deep understanding of cloud local services construction methods.

By completing this project, now you have a working image hosting service that you can increase and shield. You can add features such as verification, change size, or even better front and interface with the drag and drop UI. Most importantly, you have experienced how modern software growth and deployment fit together.

You may also like

Leave a Comment

At Skillainest, we believe the future belongs to those who embrace AI, upgrade their skills, and stay ahead of the curve.

Get latest news

Subscribe my Newsletter for new blog posts, tips & new photos. Let's stay updated!

@2025 Skillainest.Designed and Developed by Pro