存储文件到 AW3 S3

添加依赖

    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>s3</artifactId>
      <version>2.17.100</version> <!-- 请检查并使用最新版本 -->
    </dependency>

添加配置

AWS_S3_ACCESS_KEY_ID=
AWS_S3_SECRET_ACCESS_KEY=
AWS_S3_REGION_NAME=us-west-1
AWS_S3_BUCKET_NAME=rumiapp

AwsS3Utils 文件上传工具类

package com.litongjava.tio.boot.admin.utils;

import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.http.ContentTypeUtils;

import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class AwsS3Utils {

  public static final String urlFormat = "https://%s.s3.us-west-1.amazonaws.com/%s";
  public static final String bucketName = EnvUtils.get("AWS_S3_BUCKET_NAME");
  public static final String regionName = EnvUtils.get("AWS_S3_REGION_NAME");

  public static PutObjectResponse upload(S3Client client, String bucketName, String targetName, byte[] fileContent,
      String suffix) {
    try {
      String contentType = ContentTypeUtils.getContentType(suffix);
      PutObjectRequest putOb = PutObjectRequest.builder().bucket(bucketName).key(targetName).contentType(contentType)
          .build();

      PutObjectResponse putObject = client.putObject(putOb, RequestBody.fromBytes(fileContent));
      return putObject;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

配置类

package com.litongjava.tio.boot.admin.config;

import com.litongjava.tio.utils.environment.EnvUtils;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3ClientBuilder;

public class AwsS3Config {

  public S3Client buildClient() {
    String accessKeyId = EnvUtils.get("AWS_S3_ACCESS_KEY_ID");
    String secretAccessKey = EnvUtils.get("AWS_S3_SECRET_ACCESS_KEY");
    String regionName = EnvUtils.get("AWS_S3_REGION_NAME");

    AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
    StaticCredentialsProvider staticCredentialsProvider = StaticCredentialsProvider.create(awsCreds);

    Region region = Region.of(regionName);
    // 创建S3客户端
    S3ClientBuilder builder = S3Client.builder();

    S3Client s3 = builder.region(region) //
        .credentialsProvider(staticCredentialsProvider) //
        .build();

    return s3;
  }

}

测试工具类

package com.litongjava.tio.boot.admin.utils;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

import org.junit.Test;

import com.litongjava.tio.boot.admin.config.AwsS3Config;
import com.litongjava.tio.utils.environment.EnvUtils;

import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

public class AwsS3UtilsTest {

  @Test
  public void test() throws IOException {
    EnvUtils.load();

    Path path = java.nio.file.Paths.get("F:\\my_file\\my_photo\\kitty\\kitty-cat.jpg");

    byte[] fileContent = Files.readAllBytes(path);

    String targetName = "001.jpg"; // 上传到S3的文件名
    String suffix = "jpg";

    // 示例使用upload方法
    try (S3Client client = new AwsS3Config().buildClient();) {
      PutObjectResponse response = AwsS3Utils.upload(client, AwsS3Utils.bucketName, targetName, fileContent, suffix);
      String url = String.format(AwsS3Utils.urlFormat, AwsS3Utils.bucketName, targetName);
      System.out.println(url);
      System.out.println(response.eTag());
    }
  }

}

接受文件上传,并经相关信息保存到数据库

package com.litongjava.open.chat.services;

import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.litongjava.data.model.DbJsonBean;
import com.litongjava.data.services.DbJsonService;
import com.litongjava.data.utils.SnowflakeIdGenerator;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Db;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.open.chat.config.AwsS3Config;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.open.chat.utils.AwsS3Utils;
import com.litongjava.tio.boot.http.TioHttpContext;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.tio.utils.crypto.Md5Utils;
import com.litongjava.tio.utils.hutool.FilenameUtils;
import com.litongjava.tio.utils.resp.RespVo;

import lombok.extern.slf4j.Slf4j;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;

/**
 * Created by Tong Li <https://github.com/litongjava>
 */
@Slf4j
public class AwsS3StorageService {
  public RespVo upload(String category, UploadFile uploadFile) {
    if (StrKit.isBlank(category)) {
      category = "default";
    }
    String filename = uploadFile.getName();
    int size = uploadFile.getSize();
    byte[] fileContent = uploadFile.getData();

    return upload(category, filename, size, fileContent);
  }

  public RespVo upload(String category, String filename, int size, byte[] fileContent) {
    Kv kvResult = uploadReturnKv(category, filename, size, fileContent);
    return RespVo.ok(kvResult);
  }

  public Kv uploadReturnKv(String category, String filename, int size, byte[] fileContent) {
    // 上传文件
    long threadId = Thread.currentThread().getId();
    if (threadId > 31L) {
      threadId %= 31L;
    }

    if (threadId < 0L) {
      threadId = 0L;
    }
    long id = (new SnowflakeIdGenerator(threadId, 0L)).generateId();
    String suffix = FilenameUtils.getSuffix(filename);
    String newFilename = id + "." + suffix;

    String targetName = category + "/" + newFilename;

    Kv kvResult = uploadBytes(id, filename, targetName, fileContent, size, suffix);
    return kvResult;
  }

  public Kv uploadBytes(long id, String filename, String targetName, byte[] fileContent, int size, String suffix) {

    String etag = null;
    // 示例使用upload方法
    try (S3Client client = new AwsS3Config().buildClient();) {
      PutObjectResponse response = AwsS3Utils.upload(client, AwsS3Utils.bucketName, targetName, fileContent, suffix);
      etag = response.eTag();
    } catch (Exception e) {
      log.error("Error uploading file to Tencent COS", e);
      throw new RuntimeException(e);
    }

    // Log and save to database
    log.info("Uploaded with ETag: {}", etag);
    String md5 = Md5Utils.digestHex(fileContent);

    Kv kv = Kv.create().set("md5", md5).set("filename", filename).set("file_size", size)
        //
        .set("platform", "aws s3").set("region_name", AwsS3Utils.regionName).set("bucket_name", AwsS3Utils.bucketName)
        //
        .set("target_name", targetName).set("file_id", etag);

    String userId = TioHttpContext.getUserId();
    if (userId != null) {
      kv.set("creator", userId);
    }

    DbJsonBean<Kv> save = DbJsonService.getInstance().save(TableNames.tio_boot_admin_system_upload_file, kv);
    String downloadUrl = getUrl(AwsS3Utils.bucketName, targetName);

    Kv kvResult = Kv.create().set("id", save.getData().get("id").toString()).set("url", downloadUrl);

    return kvResult;

  }

  public String getUrl(String bucketName, String targetName) {
    return String.format(AwsS3Utils.urlFormat, AwsS3Utils.bucketName, targetName);
  }

  public Kv getUrlById(String id) {
    String sql = "select md5,bucket_name,target_name from tio_boot_admin_system_upload_file where id=? and deleted=0";
    Record record = Db.findFirst(sql, Long.parseLong(id));
    if (record == null) {
      return null;
    }
    String url = this.getUrl(record.getStr("bucket_name"), record.getStr("target_name"));
    Kv kv = record.toKv();
    kv.remove("target_name");
    kv.remove("bucket_name");
    kv.set("url", url);
    return kv;
  }

  public Kv getUrlByMd5(String md5) {
    String sql = "select id,bucket_name,target_name from tio_boot_admin_system_upload_file where md5=? and deleted=0";
    Record record = Db.findFirst(sql, md5);
    if (record == null) {
      return null;
    }
    String url = this.getUrl(record.getStr("bucket_name"), record.getStr("target_name"));
    Kv kv = record.toKv();
    kv.remove("target_name");
    kv.remove("bucket_name");
    kv.set("url", url);
    return kv;
  }
}

添加 action 中接收上传文件


package com.litongjava.open.chat.handler;
import java.util.Map;

import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.litongjava.data.model.DbJsonBean;
import com.litongjava.data.services.DbJsonService;
import com.litongjava.data.utils.DbJsonBeanUtils;
import com.litongjava.data.utils.KvUtils;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.plugin.activerecord.Record;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.open.chat.services.AwsS3StorageService;
import com.litongjava.tio.boot.http.TioHttpContext;
import com.litongjava.tio.boot.utils.TioRequestParamUtils;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.tio.http.server.model.HttpCors;
import com.litongjava.tio.http.server.util.HttpServerResponseUtils;
import com.litongjava.tio.http.server.util.Resps;
import com.litongjava.tio.utils.resp.RespVo;

public class SystemFileS3Handler {

  public HttpResponse upload(HttpRequest request) throws Exception {
    HttpResponse httpResponse = TioHttpContext.getResponse();
    HttpServerResponseUtils.enableCORS(httpResponse, new HttpCors());

    String method = request.getMethod();
    if ("OPTIONS".equals(method)) {
      return httpResponse;
    }

    UploadFile uploadFile = request.getUploadFile("file");
    String category = request.getParam("category");

    AwsS3StorageService storageService = Aop.get(AwsS3StorageService.class);
    if (uploadFile != null) {
      RespVo respVo = storageService.upload(category, uploadFile);
      return Resps.json(httpResponse, respVo);
    }
    return Resps.json(httpResponse, RespVo.ok("Fail"));
  }

  public HttpResponse getUploadRecordByMd5(HttpRequest request) throws Exception {
    HttpResponse httpResponse = TioHttpContext.getResponse();
    HttpServerResponseUtils.enableCORS(httpResponse, new HttpCors());

    String method = request.getMethod();
    if ("OPTIONS".equals(method)) {
      return httpResponse;
    }

    // 调用DbJsonService查询数据
    Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
    Kv kv = KvUtils.camelToUnderscore(map);
    DbJsonBean<Record> jsonBean = DbJsonService.getInstance().get(TableNames.tio_boot_admin_system_upload_file, kv);
    DbJsonBean<Kv> dbJsonBean = DbJsonBeanUtils.recordToKv(jsonBean);

    return Resps.json(httpResponse,
        RespVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg()));
  }

  public HttpResponse getUrl(HttpRequest request) throws Exception {
    HttpResponse httpResponse = TioHttpContext.getResponse();
    HttpServerResponseUtils.enableCORS(httpResponse, new HttpCors());

    String method = request.getMethod();
    if ("OPTIONS".equals(method)) {
      return httpResponse;
    }

    AwsS3StorageService storageService = Aop.get(AwsS3StorageService.class);
    RespVo respVo = null;
    String id = request.getParam("id");

    String md5 = request.getParam("md5");
    if (StrKit.notBlank(id)) {
      // id,md5,name,url,
      Kv kv = storageService.getUrlById(id);
      if (kv == null) {
        respVo = RespVo.fail();
      } else {
        respVo = RespVo.ok(kv);
      }

    } else if (StrKit.notBlank(md5)) {
      Kv kv = storageService.getUrlByMd5(md5);
      if (kv == null) {
        respVo = RespVo.fail();
      } else {
        respVo = RespVo.ok(kv);
      }
    } else {
      respVo = RespVo.fail("id or md5 can not be empty");
    }

    return Resps.json(httpResponse, respVo);
  }
}

配置路由

SystemFileS3Handler systemFileS3Handler = Aop.get(SystemFileS3Handler.class);

r.add("/api/system/file/s3/upload", systemFileS3Handler::upload);
r.add("/api/system/file/s3/md5", systemFileS3Handler::getUploadRecordByMd5);
r.add("/api/system/file/s3/url", systemFileS3Handler::getUrl);

测试

测试上传

curl --location --request POST 'http://localhost:8100/tio-boot-admin/api/system/file/upload/s3' \
--header 'authorization: 123456' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------946279255222128586808179' \
--header 'Cookie: PHPSESSID=f1aa9dbe6c8e4d06aa5bc59d3b7b43b2' \
--form 'file=@"F:\\my_file\\my_photo\\aliyun\\配置.png"' \
--form 'category="sjsu/professor"'

返回数据

{
  "code": 1,
  "data": {
    "id": "394502029835268096",
    "url": "https://rumiapp.s3.us-west-1.amazonaws.com/sjsu/professor/394502024734994432.png"
  },
  "ok": true
}