Firebase Storage

添加一个 Bucket

创建 bucket

Firebase Storage 使用存储桶(Bucket)来组织和存储您的文件。如果您刚创建了 Firebase 项目,Firebase 会为您自动创建一个默认的存储桶。

  • 您可以在 Firebase 控制台的 Storage 部分查看和管理您的存储桶。
  • 要添加或管理存储桶,请访问Firebase 控制台open in new window中的 Storage 部分,并按需创建或配置存储桶。

简单的上传文件示例

配置 bucket

String bucketName = EnvUtils.getStr("BUCKET_NAME");

@SuppressWarnings("deprecation")
FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
String bucketName = EnvUtils.getStr("BUCKET_NAME");
builder.setStorageBucket(bucketName + ".appspot.com");

以下是一个简单的 Java 示例,展示了如何上传文件到 Firebase Storage:

package com.litongjava.open.chat.config;

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

import org.junit.Before;
import org.junit.Test;

import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.firebase.cloud.StorageClient;
import com.litongjava.tio.utils.environment.EnvUtils;

public class FirebaseUploadFileTest {

  @Before
  public void before() {
    EnvUtils.load();
    try {
      new FirebaseAppConfiguration().config();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  @Test
  public void testUploadFile() {

    Bucket bucket = StorageClient.getInstance().bucket();

    String localFileName = "F:\\my_file\\my_photo\\kitty\\kitty-cat.jpg";
    Path path = Paths.get(localFileName);
    byte[] fileContent = null;
    try {
      fileContent = Files.readAllBytes(path);
    } catch (IOException e) {
      e.printStackTrace();
    }

    String targetName = "public/images/test.png";
    BlobId blobId = BlobId.of(bucket.getName(), targetName);
    BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType("image/png").build();

    Storage storage = bucket.getStorage();
    Blob blob = storage.create(blobInfo, fileContent);
    System.out.println("File uploaded to Firebase Storage: " + blob.getMediaLink());
  }
}

输出的地址访问失败

https://storage.googleapis.com/download/storage/v1/b/rumi-bdb43.appspot.com/o/public%2Fimages%2Ftest.png?generation=1719389554988278&alt=media

使用下面的地址访问成功

https://firebasestorage.googleapis.com/v0/b/rumi-bdb43.appspot.com/o/public%2Fimages%2Ftest.png?alt=media

请确保将your-bucket-name.appspot.com替换为您的实际存储桶名称,path/to/your/local/file.png替换为您希望上传的文件的本地路径,以及public/images/yourImageName.png替换为您希望在 Firebase Storage 中存储文件的路径。

将上传信息保存到数据库

将上传信息保存到数据库是上传文件流程的一个重要环节,它不仅能帮助我们跟踪文件的上传记录,还能为文件的管理、检索提供便利。在本节中,我们将通过一个示例,详细介绍如何在 Java 应用中实现这一功能,并将文件上传信息保存到数据库。

数据库表设计

参考 24.1

文件上传处理逻辑

  • 在文件上传处理的逻辑中,我们首先将文件上传到 Firebase Storage,然后将上传的文件信息保存到数据库中。下面是处理文件上传和保存信息到数据库的 Java 代码示例.
  • 对于需要获取上传文件的 URL 以供前端下载或展示的情况,我们可以通过查询数据库获取到文件的bucket_nametarget_name,进而构造出文件的访问 URL:
package com.litongjava.tio.boot.admin.services;

import com.google.cloud.storage.Blob;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.BlobInfo;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.firebase.cloud.StorageClient;
import com.jfinal.kit.Kv;
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.tio.boot.admin.costants.TableNames;
import com.litongjava.tio.http.common.UploadFile;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.http.ContentTypeUtils;
import com.litongjava.tio.utils.resp.RespVo;

import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.crypto.digest.MD5;
import lombok.extern.slf4j.Slf4j;

/**
 * Created by Tong Li <https://github.com/litongjava>
 */
@Slf4j
public class GoogleStorageService {
  String bucketName = EnvUtils.getStr("BUCKET_NAME");

  public RespVo uploadImageToGoogle(UploadFile uploadFile) {
    String filename = uploadFile.getName();
    String suffix = FileNameUtil.getSuffix(filename);
    String contentType = ContentTypeUtils.getContentType(suffix);

    byte[] fileContent = uploadFile.getData();
    //int size = uploadFile.getSize();

    return uploadImageBytes(fileContent, filename, suffix, contentType);
  }

  public RespVo uploadImageBytes(byte[] fileContent, String filename, String suffix, String contentType) {
    return uploadBytes(fileContent, filename, suffix, "public/images", contentType);
  }

  public RespVo uploadBytes(byte[] fileContent, String filename, String suffix, String folderName,
                            String contentType) {

    // 上传文件
    long threadId = Thread.currentThread().getId();
    if (threadId > 31L) {
      threadId %= 31L;
    }

    if (threadId < 0L) {
      threadId = 0L;
    }
    long id = (new SnowflakeIdGenerator(threadId, 0L)).generateId();

    String newFilename = id + "." + suffix;

    String targetName = folderName + "/" + newFilename;


    uploadBytesToGoogle(fileContent, targetName, contentType);

    // 存入到数据库
    String md5 = MD5.create().digestHex(fileContent);
    Kv kv = Kv.create();
    kv.set("md5", md5);
    kv.set("filename", filename);
    kv.set("file_size", fileContent.length);
    kv.set("platform", "google");
    kv.set("bucket_name", bucketName);
    String replaceTargetName = replaceTargetName(targetName);
    kv.set("target_name", replaceTargetName);
    kv.set("file_id", id);

    DbJsonBean<Kv> save = DbJsonService.getInstance().save(TableNames.tio_boot_admin_system_upload_file, kv);

    // 下载地址
    String downloadUrl = getUrl(bucketName, replaceTargetName);
    Kv kv1 = Kv.create();
    kv1.set("id", save.getData().get("id") + "");
    kv1.set("url", downloadUrl);

    // 返回RespVo
    return RespVo.ok(kv1);
  }

  public Blob uploadBytesToGoogle(byte[] fileContent, String targetName, String contentType) {
    Bucket bucket = StorageClient.getInstance().bucket();
    BlobId blobId = BlobId.of(bucket.getName(), targetName);

    BlobInfo blobInfo = BlobInfo.newBuilder(blobId).setContentType(contentType).build();
    Storage storage = bucket.getStorage();
    Blob blob = storage.create(blobInfo, fileContent);
    log.info("blob:{}", blob);
    return blob;

  }

  private String replaceTargetName(String targetName) {
    return targetName.replace("/", "%2F");
  }

  public String getUrlByFileId(long fileId) {
    String sql = "select bucket_name,target_name from " + TableNames.tio_boot_admin_system_upload_file + " where id=?";
    Record record = Db.findFirst(sql, fileId);

    return getUrl(record.getStr("bucket_name"), record.getStr("target_name"));
  }

  public String getUrl(String name, String targetName) {

    String template = "https://firebasestorage.googleapis.com/v0/b/%s.appspot.com/o/%s?alt=media";
    return String.format(template, name, targetName);
  }
}

handler

server 编写完成后对外提供一个 handler,为前端提供 2 个 endpont 用于上传文件和获取文件地址

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

import com.litongjava.jfinal.aop.Aop;
import com.litongjava.tio.boot.admin.services.GoogleStorageService;
import com.litongjava.tio.boot.http.TioControllerContext;
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 SystemFileFirebaseHandler {

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

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

    UploadFile uploadFile = request.getUploadFile("file");
    GoogleStorageService googleStorageService = Aop.get(GoogleStorageService.class);
    if (uploadFile != null) {
      RespVo respVo = googleStorageService.uploadImageToGoogle(uploadFile);
      return Resps.json(httpResponse, respVo);

    }
    return Resps.json(httpResponse, RespVo.ok("Fail"));
  }

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

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

    long fileId = Long.parseLong(request.getParam("id"));
    GoogleStorageService googleStorageService = Aop.get(GoogleStorageService.class);
    String url = googleStorageService.getUrlByFileId(fileId);
    return Resps.json(httpResponse, RespVo.ok(url));
  }
}

注册 handler

HttpReqeustSimpleHandlerRoute r = TioBootServer.me().getHttpReqeustSimpleHandlerRoute();

SystemFileFirebaseHandler systemFileFirebaseHandler = Aop.get(SystemFileFirebaseHandler.class);
r.add("/api/system/file/uploadToGoogle", systemFileFirebaseHandler::upload);
r.add("/api/system/file/firebase/getUrl", systemFileFirebaseHandler::getUrl);

http api

接下来,我们将详细介绍如何通过 API 接口上传图片到 Google Firebase Storage,并如何获取上传图片的 URL。我们会使用两个 RESTful API 接口:一个用于上传图片,另一个用于获取图片的访问 URL。

1. 上传图片到 Google Firebase Storage

API 端点


POST /api/system/file/uploadToGoogle

请求

要上传图片,您需要使用multipart/form-data类型的 POST 请求,并将图片文件作为请求的一部分。以下是使用curl命令上传图片的示例:

curl --location --request POST 'https://ip/tio-boot-admin/api/system/file/uploadToGoogle' \
--header 'Content-Type: multipart/form-data; boundary=--------------------------416606819414649391318961' \
--form 'file=@"C:\\Users\\Administrator\\Pictures\\gpt-translate.png"'

请确保将ip替换为您的服务器地址,以及调整文件路径至您的图片位置。

响应

上传成功后,服务器会返回一个包含图片 ID 和访问 URL 的 JSON 响应:

{
  "code": 1,
  "data": {
    "id": "368264036325801984",
    "url": "https://firebasestorage.googleapis.com/v0/b/{bucketName}/o/public%2Fimages%2F368264030806097920.png?alt=media"
  },
  "ok": true
}

id是数据库中记录的唯一标识符,url是图片在 Google Firebase Storage 的访问 URL。

2. 获取图片的 URL

一旦图片上传到 Firebase Storage,您可能需要根据图片的 ID 获取其访问 URL。

API 端点

GET /api/system/file/firebase/getUrl

请求

通过将图片 ID 作为查询参数传递,您可以请求图片的 URL。以下是使用curl获取图片 URL 的示例:

curl --location --request GET 'https://ip/tio-boot-admin/api/system/file/getGoogleFileUrl?id=368264036325801984'

同样,请确保将ip替换为您的服务器地址,并使用正确的图片 ID 作为请求参数。

响应

服务器将返回一个包含图片 URL 的 JSON 响应:

{
  "code": 1,
  "url": "https://firebasestorage.googleapis.com/v0/b/{bucketName}/o/public%2Fimages%2F368264030806097920.png?alt=media",
  "ok": true
}

这个 URL 可以直接用于 Web 页面或移动应用中,以展示或下载图片。

总结

通过上述两个 API 端点,我们能够实现一个完整的图片上传与检索流程。上传接口允许用户将图片上传到 Google Firebase Storage,并将上传信息保存到数据库中。而获取 URL 接口则提供了一种方式,通过数据库中保存的 ID 来检索并获取图片的访问 URL。这样的设计不仅简化了前端的工作流程,也提高了后端数据管理的效率和安全性。希望这篇文档能够帮助您更好地理解和实现基于 Google Firebase Storage 的图片上传与管理功能。