Backend Development 10 min read

How to Build a Self‑Hosted Image Hosting Service with Vue, Ant Design Vue, and UCloud US3

This tutorial walks through creating your own image‑hosting site by setting up a Vue front‑end with Ant Design Vue, configuring a UCloud US3 bucket, and implementing Spring Boot backend code to handle file uploads, generate signed URLs, and return structured responses.

Top Architect
Top Architect
Top Architect
How to Build a Self‑Hosted Image Hosting Service with Vue, Ant Design Vue, and UCloud US3

The article explains why building a personal image‑hosting service is useful despite existing public image hosts, and then provides a step‑by‑step guide.

Front‑end environment preparation : install Node.js, use the Taobao npm registry with sudo npm install -g cnpm --registry=https://registry.npm.taobao.org , install Vue CLI via cnpm install -g @vue/cli , create a project with vue create xiaotuwo , add Ant Design Vue using cnpm install ant-design-vue --save , and start the dev server with cd xiaotuwo then npm run serve , confirming it works at http://localhost:8080 .

Front‑end code : modify the upload component by copying an a-upload snippet from Ant Design Vue, set its action to your server endpoint (e.g., http://localhost:8887/api/images/upload ), and enhance handlePreview to copy the image link to the clipboard.

Server‑side environment : register for a UCloud US3 account, create a bucket (e.g., xiaotuwo.cn-bj.ufileos.com ), generate public/private keys, and add the necessary properties (server.port, ucloud.ufile.*, ucloud.uaicensor.*) to application.properties .

Backend controller (Spring Boot):

@PostMapping("/api/files/upload")
@ResponseBody
public FileDTO upload(HttpServletRequest request) {
    FileDTO resultFileDTO = new FileDTO();
    MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
    MultipartFile file = multipartRequest.getFile("file");
    long start = System.currentTimeMillis();
    try {
        if (file == null) {
            resultFileDTO.setStatus("error");
        }
        FileDTO fileDTO = uCloudProvider.uploadWithExpired(file.getInputStream(), file.getContentType(), Objects.requireNonNull(file.getOriginalFilename()));
        // logging omitted for brevity
        resultFileDTO.setName(fileDTO.getName());
        resultFileDTO.setUrl(fileDTO.getUrl());
        resultFileDTO.setThumbUrl(fileDTO.getUrl());
        resultFileDTO.setStatus("done");
    } catch (Exception e) {
        // error handling omitted
        resultFileDTO.setStatus("error");
    }
    return resultFileDTO;
}

UCloudProvider upload logic generates a UUID file name, creates an ObjectAuthorization with the keys, uploads via UfileClient.object(...).putObject(...).execute() , obtains a signed download URL, replaces http with https , and returns a FileDTO containing the URL and name.

public FileDTO upload(InputStream fileStream, String mimeType, String fileName) {
    String generatedFileName;
    String[] filePaths = fileName.split("\\.");
    if (filePaths.length > 1) {
        generatedFileName = UUID.randomUUID().toString() + "." + filePaths[filePaths.length - 1];
    } else {
        throw new ErrorCodeException(ErrorCode.FILE_UPLOAD_FAIL);
    }
    long start = System.currentTimeMillis();
    try {
        ObjectAuthorization objectAuthorization = new UfileObjectLocalAuthorization(publicKey, privateKey);
        ObjectConfig config = new ObjectConfig(uploadDomainPrivate);
        PutObjectResultBean response = UfileClient.object(objectAuthorization, config)
            .putObject(fileStream, mimeType)
            .nameAs(generatedFileName)
            .toBucket(bucketNamePrivate)
            .execute();
        if (response != null && response.getRetCode() == 0) {
            String url = UfileClient.object(objectAuthorization, new ObjectConfig(downloadDomainPrivate))
                .getDownloadUrlFromPrivateBucket(generatedFileName, bucketNamePrivate, 24 * 60 * 60)
                .createUrl();
            FileDTO fileDTO = new FileDTO();
            fileDTO.setUrl(url.replace("http", "https"));
            fileDTO.setName(generatedFileName);
            return fileDTO;
        } else {
            throw new ErrorCodeException(ErrorCode.FILE_UPLOAD_FAIL);
        }
    } catch (UfileClientException | UfileServerException e) {
        throw new ErrorCodeException(ErrorCode.FILE_UPLOAD_FAIL);
    }
}

DTO definition :

@Data
public class FileDTO {
    private String name;
    private String status;
    private String url;
    private String thumbUrl;
}

All source code is available at https://github.com/xiaotuwo/xiaotuwo-client (frontend) and https://github.com/xiaotuwo/xiaotuwo-server (backend). The article concludes with an invitation to join the community for further questions.

Frontend DevelopmentBackend DevelopmentVueAnt Design VueImage HostingUCloud US3
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.