存储文件到 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.open.chat.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);
    }
  }

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

}

配置类

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.db.TableInput;
import com.litongjava.db.TableResult;
import com.litongjava.db.activerecord.Record;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.open.chat.config.AwsS3Config;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.open.chat.dao.SystemUploadFileDao;
import com.litongjava.open.chat.model.UploadResultVo;
import com.litongjava.open.chat.utils.AwsS3Utils;
import com.litongjava.table.services.ApiTable;
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.snowflake.SnowflakeIdUtils;

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

@Slf4j
public class AwsS3StorageService {
  public RespBodyVo upload(String category, UploadFile uploadFile) {
    if (StrKit.isBlank(category)) {
      category = "default";
    }
    String filename = uploadFile.getName();
    int size = uploadFile.getSize();
    byte[] fileContent = uploadFile.getData();

    return RespBodyVo.ok(uploadBytes(category, filename, size, fileContent));
  }

  public UploadResultVo uploadBytes(String category, String originFilename, int size, byte[] fileContent) {
    // 上传文件
    long id = SnowflakeIdUtils.id();
    String suffix = FilenameUtils.getSuffix(originFilename);
    String newFilename = id + "." + suffix;

    String targetName = category + "/" + newFilename;

    return uploadBytes(id, originFilename, targetName, fileContent, size, suffix);
  }

  /**
   * @param id
   * @param originFilename
   * @param targetName
   * @param fileContent
   * @param size
   * @param suffix
   * @return
   */
  public UploadResultVo uploadBytes(long id, String originFilename, String targetName, byte[] fileContent, int size, String suffix) {
    String md5 = Md5Utils.digestHex(fileContent);
    Record record = Aop.get(SystemUploadFileDao.class).getFileBasicInfoByMd5(md5);
    if (record != null) {
      log.info("select table reuslt:{}", record.toMap());
      id = record.getLong("id");
      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);
      kv.set("md5", md5);
      return new UploadResultVo(id, originFilename, url, md5);
    } else {
      log.info("not found from cache table:{}", md5);
    }

    String etag = null;
    try (S3Client client = new AwsS3Config().buildClient();) {
      PutObjectResponse response = AwsS3Utils.upload(client, AwsS3Utils.bucketName, targetName, fileContent, suffix);
      etag = response.eTag();
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }

    // Log and save to database
    log.info("Uploaded with ETag: {}", etag);

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

    TableResult<Kv> save = ApiTable.save(TableNames.tio_boot_admin_system_upload_file, kv);
    String downloadUrl = getUrl(AwsS3Utils.bucketName, targetName);

    return new UploadResultVo(save.getData().getLong("id"), originFilename, downloadUrl, md5);

  }

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

  public UploadResultVo getUrlById(String id) {
    return getUrlById(Long.parseLong(id));
  }

  public UploadResultVo getUrlById(long id) {
    Record record = Aop.get(SystemUploadFileDao.class).getFileBasicInfoById(id);
    if (record == null) {
      return null;
    }
    String url = this.getUrl(record.getStr("bucket_name"), record.getStr("target_name"));
    String originFilename = record.getStr("fielename");
    String md5 = record.getStr("md5");
    return new UploadResultVo(id, originFilename, url, md5);
  }

  public UploadResultVo getUrlByMd5(String md5) {
    Record record = Aop.get(SystemUploadFileDao.class).getFileBasicInfoByMd5(md5);
    if (record == null) {
      return null;
    }
    Long id = record.getLong("id");
    String url = this.getUrl(record.getStr("bucket_name"), record.getStr("target_name"));
    Kv kv = record.toKv();
    kv.set("url", url);
    kv.set("md5", md5);
    String originFilename = record.getStr("filename");
    return new UploadResultVo(id, originFilename, url, md5);
  }
}

添加 action 中接收上传文件

import java.util.Map;

import com.jfinal.kit.Kv;
import com.jfinal.kit.StrKit;
import com.litongjava.db.TableInput;
import com.litongjava.db.TableResult;
import com.litongjava.db.activerecord.Record;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.open.chat.constants.TableNames;
import com.litongjava.open.chat.model.UploadResultVo;
import com.litongjava.open.chat.services.AwsS3StorageService;
import com.litongjava.table.services.ApiTable;
import com.litongjava.table.utils.TableInputUtils;
import com.litongjava.table.utils.TableResultUtils;
import com.litongjava.tio.boot.http.TioRequestContext;
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.CORSUtils;
import com.litongjava.tio.http.server.util.Resps;

public class SystemFileS3Handler {

  public HttpResponse upload(HttpRequest request) throws Exception {
    HttpResponse httpResponse = TioRequestContext.getResponse();
    CORSUtils.enableCORS(httpResponse, new HttpCors());
    UploadFile uploadFile = request.getUploadFile("file");
    String category = request.getParam("category");

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

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

    Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
    TableInput kv = TableInputUtils.camelToUnderscore(map);
    // 调用ApiTable查询数据
    TableResult<Record> jsonBean = ApiTable.get(TableNames.tio_boot_admin_system_upload_file, kv);
    TableResult<Kv> TableResult = TableResultUtils.recordToKv(jsonBean);

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

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

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

    String md5 = request.getParam("md5");
    if (StrKit.notBlank(id)) {
      // id,md5,name,url,
      UploadResultVo uploadResultVo = storageService.getUrlById(id);
      if (uploadResultVo == null) {
        respBodyVo = RespBodyVo.fail();
      } else {
        respBodyVo = RespBodyVo.ok(uploadResultVo);
      }

    } else if (StrKit.notBlank(md5)) {
      UploadResultVo uploadResultVo = storageService.getUrlByMd5(md5);
      if (uploadResultVo == null) {
        respBodyVo = RespBodyVo.fail();
      } else {
        respBodyVo = RespBodyVo.ok(uploadResultVo);
      }
    } else {
      respBodyVo = RespBodyVo.fail("id or md5 can not be empty");
    }

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

配置路由

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
}