写入 Firebase 存储的零字节文件

Zero btye file written to Firebase storage

我正在将文件从 vuejs 应用程序上传到 firebase 存储。我可以成功写入 firebase 存储,但文件的字节数为零。

文件通过 GraphQL 突变发送到后端:

uploadStream: {
  type: GraphQLBoolean,
  args: {
    id: {
      type: GraphQLString
    },
    file: {
      type: GraphQLUpload
    }
  },
  // eslint-disable-next-line no-unused-vars
  resolve: async (root, { id, file }, context) => {
    const { filename, mimetype, createReadStream } = await file
    const stream = createReadStream()
    const ext = filename.substr(filename.lastIndexOf('.') + 1)
    storageService.upload(id, ext, mimetype, stream)
  }
}

然后我这样写入存储:

const upload = async (filename, ext, mimetype, stream) => {
  const metadata = {
    metadata: {
      // This line is very important. It's to create a download token.
      firebaseStorageDownloadTokens: uuid()
    },
    contentType: mimetype
  }
  filename = `profiles/${filename}.${ext}`
  stream.pipe(
    bucket
      .file(filename, {
        gzip: true,
        metadata: metadata
      })
      .createWriteStream()
      .end()
  )
}

我使用 firebase-admin SDK 正在检索存储桶:

const firebaseConfig = {
  credential: firebase.credential.cert(JSON.parse(firebase_credential)),
  databaseURL: firebase_db,
  storageBucket: firebase_storage
}

firebase.initializeApp(firebaseConfig)

const db = firebase.firestore()
const bucket = firebase.storage().bucket()

我不明白为什么上面的代码将零字节文件写入存储。

创建到 Cloud Storage 的写入流后,您立即将其关闭。

bucket
  .file(filename, {
    gzip: true,
    metadata: metadata
  })
  .createWriteStream()  // <- opens write stream
  .end()                // <- immediately closes it

如果 stream 和存储文件的流具有 "error" 事件的侦听器,您会更早发现这一点。

只需删除 .end(),您的代码就会再次运行:

const upload = async (filename, ext, mimetype, stream) => {
  const metadata = {
    metadata: {
      // This line is very important. It's to create a download token.
      firebaseStorageDownloadTokens: uuid()
    },
    contentType: mimetype
  }
  filename = `profiles/${filename}.${ext}`
  stream.pipe(
    bucket
      .file(filename, {
        gzip: true,
        metadata: metadata
      })
      .createWriteStream()
  )
}

但是,因为您在 upload() 方法上使用 async,这意味着您希望它 return 一个 Promise 在上传完成时解析或在失败时拒绝。

const upload = async (filename, ext, mimetype, stream) => {
  const metadata = {
    metadata: {
      // This line is very important. It's to create a download token.
      firebaseStorageDownloadTokens: uuid()
    },
    contentType: mimetype
  }
  filename = `profiles/${filename}.${ext}`

  const storageStream = bucket
    .file(filename, {
      gzip: true,
      metadata: metadata
    })
    .createWriteStream();

  return new Promise((resolve, reject) => {
    storageStream.once("finish", resolve); // resolve when written
    storageStream.once("error", reject);   // reject when either stream errors
    stream.once("error", reject);

    stream.pipe(storageStream);            // pipe the data
  });
}

这意味着 resolve: 需要更新为:

resolve: async (root, { id, file }, context) => {
  const { filename, mimetype, createReadStream } = await file
  const stream = createReadStream()
  const ext = filename.substr(filename.lastIndexOf('.') + 1)
  return storageService.upload(id, ext, mimetype, stream)
}