【问题标题】:How to upload images to an auto-generated doc through add() in firestore如何通过 firestore 中的 add() 将图像上传到自动生成的文档
【发布时间】:2021-08-17 18:09:06
【问题描述】:

我正在创建一个动态页面板。

并且这个页面上的url是通过firestore add()创建的。

我希望允许用户在此公告板上创建帖子时上传图片。

有没有办法将图像放入 firestore 字段?

如果不是,我应该让他们将图像上传到存储吗? 如果是这样,我可以从通过 add() 创建的页面加载图像吗?

【问题讨论】:

标签: firebase google-cloud-firestore firebase-storage


【解决方案1】:

由于您未能指定目标语言,我将假设您使用的是 JavaScript Web SDK。在每个 SDK 中,add(data)doc().set(data) 的语法糖。因此,要生成我们可以在文件上传中使用的表单 ID,我们可以使用:

const formRef = firebase.firestore().collection("forms").doc();

最终,在您的数据库中存储图像时,您有两种选择。

不推荐:将资源嵌入 Cloud Firestore 文档中

在文档中存储二进制文件的最简单方法之一是使用Data URL。这些 URL 获取二进制数据,将其编码为 Base64,添加一些关于存储数据的元数据,然后生成如下所示的字符串:

// Data URL for a PNG image of a single #FFFFFF pixel
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAAAASUVORK5CYII="

然后可以将这些数据 URL 存储在文档中(例如 /forms/{formId})或其子集合之一中(例如 /forms/{formId}/attachments/{attachmentId})。通过以这种方式存储图像,由于 Base64 编码,文件大小会膨胀大约 25% 或更多。

使用此方法,您可以使用Cloud Firestore Security Rules 控制对图像的访问,但它还需要您实现从数据库中获取图像并将其插入网页的逻辑。

要将添加到表单的图像转换为数据 URL,请参阅FileReader#readAsDataURL() 的文档。您还可以将 Web 画布转换为数据 URL,如 question thread 所示。

function readFileAsDataURL(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.addEventListener("load", function () { resolve(this.result) }, false);
    reader.addEventListener("error", function () { resolve(this.error) }, false);

    reader.readAsDataURL(file);
  });
}

async function onSubmitForm(e) {
  const user = firebase.auth().currentUser;

  if (!user) {
    setErrorMessage("You must be signed in!");
    return false;
  }

  const uid = user.uid;
  const files = document.querySelector('input[type=file]').files;
  const dataURLs = files
    ? await Promise.all([].map.call(files, readFileAsDataURL))
    : [];

  // prepare database stuff
  const db = firebase.firestore();
  const batch = db.batch();

  // create form reference
  const formRef = db.collection("forms").doc();

  // assemble & queue upload of each attachment
  const attachmentsColRef = formRef.collection("attachments");
  const attachmentIds = dataURLs.map((url, i) => {
    const file = files[i];
    const attachmentRef = attachmentsColRef.doc();
    const attachmentData = {
      id: attachmentRef.id, // optional
      lastModified: firebase.firestore.Timestamp.fromMillis(file.lastModified),
      name: file.name,
      src: url,
      type: file.type
    }

    batch.set(attachmentRef, attachmentData);
    return attachmentRef.id;
  }

  // assemble form data
  const formData = {
    attachments: attachmentIds,
    content: "this is my first post",
    createdAt: firebase.firestore.FieldValue.serverTimestamp(),
    id: formRef.id, // optional
    name: "john",
    title: "Hello world! I'm John",
    uid,
    updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
    views: 0
  }
  
  // queue upload of form data
  batch.set(formRef, formData);

  // do the upload
  return batch.commit()
    .then(() => {
      console.log(`Successfully created Form #${formRef.id}!`);
      return true;
    })
    .catch((error) => {
      setErrorMessage("Failed to submit form data!");
      console.error(`Failed to create Form #${formRef.id}!`, error);
      return false;
    });
}

推荐:将文件存储在云存储(或其他合适的二进制数据存储)中

这部分将专注于谷歌云存储,但也可以应用于其他基于应用程序的文件存储服务(如 Amazon Elastic File System、Amazon S3 Buckets、DigitalOcean Spaces 等)和以消费者为中心的文件存储服务(例如 Google Drive、OneDrive、Dropbox、WeTransfer 等)。

与 Cloud Firestore 类似,您可以将图像存储在这个“二进制对象数据库”中,并使用Cloud Storage Security Rules 保护它。但是,与 Cloud Firestore 相比,此存储方法针对以多种不同形式存储二进制数据进行了优化,并提供了许多特定于文件处理的重点功能,例如限制上传的文件、恢复上传、浏览器缓存信息、保留策略(例如删除所有超过 1 年的文件)和文件版本控制(如果您想使用它)。

可以通过 Cloud Storage 和 Firebase SDK 或 URL 访问存储在 Cloud Storage 中的文件(如果文件是私有文件,则在访问 URL 时需要提供访问令牌)。

这些 URL 通常采用以下形式:

// public file, in Google Cloud Storage
"https://storage.googleapis.com/BUCKET_NAME/OBJECT_NAME"

// private file, with an access token, in Firebase Cloud Storage
"https://firebasestorage.googleapis.com/v0/b/PROJECT_ID.appspot.com/o/OBJECT_NAME.png?alt=media&token=ACCESS_TOKEN"
async function onSubmitForm(e) {
  const user = firebase.auth().currentUser;

  if (!user) {
    setErrorMessage("You must be signed in!");
    return false;
  }

  const uid = user.uid;
  const files = document.querySelector('input[type=file]').files;

  // prepare database stuff
  const db = firebase.firestore();
  const batch = db.batch();

  // create form reference
  const formRef = db.collection("forms").doc();

  // upload and assemble the document for each attachment
  const attachmentsColRef = formRef.collection("attachments");
  const attachmentIds = !files ? [] : await Promise.all(
    [].map.call(files, async (file) => {
      const attachmentRef = attachmentsColRef.doc();

      const storageRef = firebase.storage()
        .ref(`formUploads/${formRef.id}`)
        .child(`attachments/${attachmentRef.id}`)
        .child(uid)
        .child(file.name);

      const uploadResult = await storageRef.put(file);
      const url = await storageRef.getDownloadURL();
      
      const attachmentData = {
        id: attachmentRef.id, // optional
        lastModified: firebase.firestore.Timestamp.fromMillis(file.lastModified),
        name: file.name,
        src: url,
        type: file.type
      }

      batch.set(attachmentRef, attachmentData);
      return attachmentRef.id;
    })
  );

  // assemble form data
  const formData = {
    attachments: attachmentIds,
    content: "this is my first post",
    createdAt: firebase.firestore.FieldValue.serverTimestamp(),
    id: formRef.id, // optional
    name: "john",
    title: "Hello world! I'm John",
    uid,
    updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
    views: 0
  }
  
  // queue upload of form data
  batch.set(formRef, formData);

  // do the upload
  return batch.commit()
    .then(() => {
      console.log(`Successfully created Form #${formRef.id}!`);
      return true;
    })
    .catch((error) => {
      setErrorMessage("Failed to submit form data!");
      console.error(`Failed to create Form #${formRef.id}!`, error);
      return false;
    });
}

【讨论】:

  • 感谢您的回答。我用vue3。我会使用你推荐的方法。
猜你喜欢
  • 2018-08-27
  • 2018-06-17
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
  • 2018-06-14
  • 1970-01-01
  • 2019-09-10
  • 2010-12-05
相关资源
最近更新 更多