我花了3块6,给自己搞了一个在线图床功能

2,126 阅读7分钟

请在此添加图片描述

快来薅羊毛:轻量云对象存储

随着社交媒体和图片分享平台的广泛应用,图床(即图片托管服务)成为了开发者和内容创作者不可或缺的一部分。图床允许用户将图片上传到云端存储,并通过 URL 进行访问,减少了用户设备上的存储空间占用,同时提供了高效的图片管理和访问方式。腾讯云的轻量对象存储(COS)为开发者提供了一个简单且高效的图床解决方案,本文将介绍如何使用腾讯云 COS 构建一个完整的在线图床功能。

请在此添加图片描述

1. 什么是图床

图床(Image Hosting Service)是指将图片存储在云端服务器上,通过 HTTP 或 HTTPS 协议提供图片访问链接的服务。图床的核心功能包括:通过图床,用户无需再将图片存储在本地服务器上,大大提高了系统的扩展性和图片访问速度。

  • 图片上传:用户可以将图片上传到云端。
  • 图片存储:图片保存在云存储上,并根据文件名生成唯一的文件路径或 URL。
  • 图片访问:用户通过 URL 来访问已上传的图片。

2. 腾讯云轻量对象存储 COS

腾讯云 COS(Cloud Object Storage)是腾讯云提供的一种云存储服务,适合存储海量非结构化数据,如图片、视频、日志等。其具备以下特点:

  • 高可靠性:COS 提供多副本存储机制,确保数据的高可用性和持久性。
  • 低成本:COS 提供按需计费服务,用户只需为实际存储和流量付费,适合各种规模的应用场景。
  • 高性能:COS 提供快速的数据访问能力,特别适合图片和视频等大数据量的存储和访问。
  • 易于集成:腾讯云提供丰富的 SDK 和 API,使得 COS 可以方便地与现有应用程序集成。

3. 构建图床的需求

一个完整的在线图床功能需要满足以下需求:在实现这些需求之前,我们需要进行一些基本的准备工作。

  1. 用户注册与认证:用户需要进行身份验证,以确保图片上传和管理是私密的。
  2. 图片上传:支持上传单张或多张图片,且上传的图片可以按需生成 URL 地址。
  3. 图片存储:上传的图片会存储在腾讯云 COS 中,支持大文件存储和快速访问。
  4. 图片管理:用户可以查看已上传的图片,支持删除或更新图片。
  5. 图片访问:通过 URL 访问已上传的图片,支持前端展示。

4. 准备工作

4.1 注册腾讯云账号

首先,前往 腾讯云官网 注册一个腾讯云账号。如果已有账号,可以直接登录。

4.2 创建腾讯云 COS 存储桶

  1. 登录腾讯云管理控制台,找到 COS 服务。
  2. 创建一个新的存储桶,并设置访问权限(公共读或私有读等)。
  3. 获取 AppID、SecretID 和 SecretKey,这些信息将用于后续的 SDK 配置和鉴权。

4.3 配置环境

为了在 Java 项目中使用腾讯云 COS,需要导入腾讯云的 SDK。以下是添加依赖的方式。

使用 Maven 管理依赖

在 pom.xml 文件中加入以下依赖:

		<dependency>
			<groupId>com.qcloud</groupId>
			<artifactId>cos_api</artifactId>
			<version>5.6.54</version>
		</dependency>

​

配置腾讯云 COS 参数

在 application.properties 或 application.yml 中配置腾讯云 COS 相关参数:

tencent:
  cos:
    secretId: xxxxxxx
    secretKey: xxxxxxxxxxxxxxxxx
    region: ap-nanjing
    bucketName: number-89757-1259242531

5. 后端实现

5.1 创建实体类

首先,我们需要创建一个实体类 Image 来存储图床中的图片信息,包括图片的 URL、文件名、文件大小等。

package com.example.demo.entity;

// 响应 DTO
public  class ResponseDto {
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

5.3 创建 COS 服务

接下来,我们创建一个 CosService 类来封装所有与腾讯云 COS 相关的操作,包括上传、删除、获取文件等功能。

package com.example.demo.service;

import com.example.demo.controller.CosImageController;
import com.example.demo.entity.ResponseDto;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.model.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

@Service
public class CosService {

    @Value("${tencent.cos.secretId}")
    private String secretId;

    @Value("${tencent.cos.secretKey}")
    private String secretKey;

    @Value("${tencent.cos.region}")
    private String region;

    @Value("${tencent.cos.bucketName}")
    private String bucketName;

    /**
     * 上传文件到 COS
     *
     * @param file    上传的文件
     * @param key     上传到 COS 的对象键
     * @return        文件的 URL 地址
     * @throws IOException
     */
    public String uploadFile(File file, String key) throws IOException {

        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        COSClient cosClient = new COSClient(cred, clientConfig);
//        // 创建上传请求
//        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, file);
//        // 执行上传
//        cosClient.putObject(putObjectRequest);
        FileInputStream fileInputStream = new FileInputStream(file);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, fileInputStream, new ObjectMetadata());
        cosClient.putObject(putObjectRequest);

        // 生成文件的 URL 地址
        return "https://" + bucketName + ".cos." + region + ".myqcloud.com/" + key;
    }

    /**
     * 删除 COS 中的文件
     *
     * @param key 文件的对象键
     */
    public void deleteFile(String key) {

        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        COSClient cosClient = new COSClient(cred, clientConfig);
        cosClient.deleteObject(bucketName, key);
    }

    /**
     * 获取 COS 中所有文件的列表
     *
     * @return 文件列表
     */
    public List<ResponseDto> listFiles() {
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        COSClient cosClient = new COSClient(cred, clientConfig);
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
                .withBucketName(bucketName)
                .withPrefix("");  // 可以设置前缀来过滤文件
        ObjectListing objectListing = cosClient.listObjects(listObjectsRequest);
        List<COSObjectSummary> aa = objectListing.getObjectSummaries();
        List<ResponseDto> result = new ArrayList<>();
        for (COSObjectSummary vv :aa){
            ResponseDto dd = new ResponseDto();
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, vv.getKey());
//        req.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)); // 设置URL有效期为1小时
            URL signedUrl = cosClient.generatePresignedUrl(req);
            dd.setUrl(signedUrl.toString());
            result.add(dd);
        }
        return result;
    }

    /**
     * 转换 MultipartFile 为 File 对象
     *
     * @param file 上传的 MultipartFile 文件
     * @return     转换后的 File 对象
     * @throws IOException
     */
    public File convertMultiPartToFile(MultipartFile file) throws IOException {
        File convFile = new File(file.getOriginalFilename());
        file.transferTo(convFile);
        return convFile;
    }

    /**
     * 关闭 COS 客户端
     */
    public void shutdown() {
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        ClientConfig clientConfig = new ClientConfig(new Region(region));
        COSClient cosClient = new COSClient(cred, clientConfig);
        cosClient.shutdown();
    }
}

5.4 创建控制器

控制器 ImageController 处理前端的请求,例如上传图片、列出图片、删除图片等。

package com.example.demo.controller;

import com.example.demo.entity.ResponseDto;
import com.example.demo.service.CosService;
import com.qcloud.cos.model.COSObjectSummary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.ResponseEntity;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.List;

@Controller
public class CosImageController {

    @Autowired
    private CosService cosService;  // 调用 COS 服务

    // 图片存储路径
    private static final String UPLOAD_DIR = "src/main/resources/static/uploads/";

    @GetMapping("/")
    public String ocrPage() {
        return "cos"; // 返回上传页面视图
    }
    // 上传图片
    @PostMapping("/image/upload")
    public String uploadImage(@RequestParam("file") MultipartFile file) throws IOException {
        // 将上传的文件保存到 COS

        // 确保上传目录存在
        Path uploadPath = Paths.get(UPLOAD_DIR);
        if (!Files.exists(uploadPath)) {
            Files.createDirectories(uploadPath);
        }

        // 获取文件输入流并存储文件
        Path filePath = uploadPath.resolve(file.getOriginalFilename());
        try (InputStream inputStream = file.getInputStream()) {
            Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
        }

        try {
            String fileName = System.currentTimeMillis() + "_" + file.getOriginalFilename();
//            String fileUrl = cosService.uploadFile(convertMultiPartToFile(file), fileName);
            String fileUrl = cosService.uploadFile(filePath.toFile(), fileName);
            System.out.printf(fileUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:/";
    }

    // 获取已上传的图片列表
    @GetMapping("/image/list")
    @ResponseBody
    public List<ResponseDto> getImageList() {
        // 这里假设已经存储了图片的 URL 和 ID
        List<ResponseDto> sss = cosService.listFiles();
        return sss;  // 你可以从数据库或 COS 获取图片信息
    }

    // 删除图片
    @DeleteMapping("/image/delete/{id}")
    public ResponseEntity<?> deleteImage(@PathVariable String id) {
        // 根据 ID 删除文件
        cosService.deleteFile(id);  // 你可以根据文件 ID 从 COS 删除文件
        return ResponseEntity.ok().build();
    }

    // 辅助方法:转换 MultipartFile 为 File
    private File convertMultiPartToFile(MultipartFile file) throws IOException {
        File convFile = new File(file.getOriginalFilename());
        file.transferTo(convFile);
        return convFile;
    }



    // 图片信息 DTO
    public static class ImageDto {
        private String id;
        private String url;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }
}

6. 前端实现

前端部分实现图床的用户界面,支持图片上传和展示。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图床管理</title>
    <!-- 引入 Bootstrap 样式 -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .image-preview {
            max-width: 100px;
            max-height: 100px;
            object-fit: cover;
        }
    </style>
</head>
<body>

<div class="container mt-5">
    <h1 class="mb-4">图床管理</h1>

    <!-- 图片上传部分 -->
    <div class="mb-4">
        <h3>上传图片</h3>
        <form action="/image/upload" method="post" enctype="multipart/form-data">
            <input type="file" class="form-control" name="file" required>
            <button type="submit" class="btn btn-primary">上传</button>
        </form>
    </div>

    <!-- 已上传的图片列表 -->
    <h3>已上传图片</h3>
    <div id="imageList" class="row"></div>
</div>

<!-- 引入 jQuery 和 Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>

        // 加载已上传的图片列表
        function loadImageList() {
            $.ajax({
                url: '/image/list',  // 后端获取图片列表的接口
                type: 'GET',
                success: function (images) {
                    $('#imageList').empty();  // 清空当前列表
                    images.forEach(function (image) {
                        $('#imageList').append(`
                            <div class="col-md-3 mb-3">
                                <div class="card">
                                    <img src="${image.url}" class="card-img-top" alt="Image" style="height: 150px; object-fit: cover;">
                                    <div class="card-body">
                                        <a href="${image.url}" target="_blank" class="btn btn-primary btn-sm">查看</a>
                                        <button class="btn btn-danger btn-sm delete-btn" data-id="${image.id}">删除</button>
                                    </div>
                                </div>
                            </div>
                        `);
                    });
                },
                error: function (xhr, status, error) {
                    alert("加载图片列表失败:" + error);
                }
            });
        }

        // 删除图片
        $(document).on('click', '.delete-btn', function () {
            var imageId = $(this).data('id');
            $.ajax({
                url: '/image/delete/' + imageId,  // 后端删除图片的接口
                type: 'DELETE',
                success: function () {
                    loadImageList();  // 更新图片列表
                },
                error: function (xhr, status, error) {
                    alert("删除失败:" + error);
                }
            });
        });

        // 初始化时加载图片列表
        loadImageList();
</script>

</body>
</html>

7. 效果展示

请在此添加图片描述

我们使用了 Spring Boot 构建后端,腾讯云 COS 作为存储服务,并实现了图片上传、删除和展示等功能。通过前端 HTML 页面,我们实现了图片上传和查看功能。通过这种方式,我们不仅可以实现一个高效、可靠的图床系统,还能够充分利用腾讯云的云存储优势,提高系统的性能和可扩展性。

​