Firebase图片上传,通过云功能(https)调整大小并保存到Firestore

时间:2018-10-09 22:50:36

标签: node.js firebase google-cloud-firestore google-cloud-functions

我正在努力实现以下目标,但是作为一个新手,我看不到自己在做什么错:

  1. 文件上传触发云功能(https.onRequest())
  2. 调整图像大小3次(100x100拇指,500x500拇指,调整原始大小以限制其大小)
  3. 将所有3张图像保存到Firebase存储桶(Google Storage)
  4. 获取3张图片各自的签名URL
  5. 将签名的URL保存到Firestore

以下代码有效,但不一致:

在我的index.js文件中,我通过以下方式创建HTTP触发器:     export const image = functions.https.onRequest(uploadImage);

那叫:

import * as admin from "firebase-admin";
import * as Busboy from "busboy";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
const cors = require("cors")({ origin: true });

const spawn = require("child-process-promise").spawn;

const gcconfig = {
  projectId: "PROJECT_ID",
  keyFilename: "key.json"
};
const gcs = require("@google-cloud/storage")(gcconfig);

export const uploadImage = (req, res) => {
  cors(req, res, () => {
    if (req.method === "POST") {
      const busboy = new Busboy({ headers: req.headers });
      const docRef = req.query.docRef;
      const fieldRef = req.query.fieldRef;
      const storageRef = req.query.storageRef;
      let fileData = null;

      // Max height and width of the thumbnail in pixels.
      const THUMB_MAX_HEIGHT = 100;
      const THUMB_MAX_WIDTH = 100;
      const THUMB_PREFIX = "thumb_";
      const RESIZED_MAX_HEIGHT = 500;
      const RESIZED_MAX_WIDTH = 500;
      const RESIZED_PREFIX = "resized_";
      const ORIGINAL_MAX_HEIGHT = 1000;
      const ORIGINAL_MAX_WIDTH = 1400;

      // Listen for event when Busboy finds a file to stream.
      busboy.on("file", (fieldname, file, filename, encoding, mimetype) => {
        const tempLocalFile = path.join(os.tmpdir(), filename);
        fileData = { file: filename, type: mimetype };

        // Download file local tmp.
        file.pipe(fs.createWriteStream(tempLocalFile));
      });

      busboy.on("finish", () => {

        // File and directory paths.
        const tempLocalFile = path.join(os.tmpdir(), fileData.file);
        const tempLocalThumbFile = path.join(os.tmpdir(),`${THUMB_PREFIX}${fileData.file}`);
        const tempLocalResizedFile = path.join(os.tmpdir(),`${RESIZED_PREFIX}${fileData.file}`);
        const origFilePath = path.normalize(path.join(storageRef, fileData.file));
        const thumbFilePath = path.normalize(path.join(storageRef, `${THUMB_PREFIX}${fileData.file}`));
        const resizedFilePath = path.normalize(path.join(storageRef, `${RESIZED_PREFIX}${fileData.file}`));

        // Cloud Storage files.
        const bucket = gcs.bucket("PROJECT_ID.appspot.com");
        const metadata = {
          contentType: fileData.type,
          "Cache-Control": "public,max-age=3600"
        };

        // Generate a thumbnail called tempLocalThumbFile
        const promise = spawn(
          "convert",
          [
            tempLocalFile,
            "-thumbnail",
            `${THUMB_MAX_WIDTH}x${THUMB_MAX_HEIGHT}>`,
            tempLocalThumbFile
          ],
          { capture: ["stdout", "stderr"] }
        );

        return promise
          .then(() => {
            // Generate a resized_ called tempLocalResizedFile
            return spawn(
              "convert",
              [
                tempLocalFile,
                "-thumbnail",
                `${RESIZED_MAX_WIDTH}x${RESIZED_MAX_HEIGHT}>`,
                tempLocalResizedFile
              ],
              { capture: ["stdout", "stderr"] }
            );
          })
          .then(() => {
            // Resize original to ensure it's not massive
            return spawn(
              "convert",
              [
                tempLocalFile,
                "-thumbnail",
                `${ORIGINAL_MAX_WIDTH}x${ORIGINAL_MAX_HEIGHT}>`,
                tempLocalFile
              ],
              { capture: ["stdout", "stderr"] }
            );
          })
          .then(() => {
            // upload images to storage
            const thumbUp = bucket.upload(tempLocalThumbFile, {
              destination: thumbFilePath,
              metadata: metadata
            });
            const resizeUp = bucket.upload(tempLocalResizedFile, {
              destination: resizedFilePath,
              metadata: metadata
            });
            const origUp = bucket.upload(tempLocalFile, {
              destination: origFilePath,
              metadata: metadata
            });

            return Promise.all([thumbUp, resizeUp, origUp]);
          })
          .then(() => {
            // Once the image has been uploaded delete the local files to free up disk space.
            fs.unlinkSync(tempLocalFile);
            fs.unlinkSync(tempLocalThumbFile);
            fs.unlinkSync(tempLocalResizedFile);

            const config = {
              action: "read",
              expires: "03-01-2500"
            };

            // Get the Signed URLs for the thumbnail and original image.
            return Promise.all([
              bucket.file(thumbFilePath).getSignedUrl(config),
              bucket.file(resizedFilePath).getSignedUrl(config),
              bucket.file(origFilePath).getSignedUrl(config)
            ]);
          })
          .then(results => {
            console.log("Got Signed URLs.", results);
            const thumbFileUrl = results[0][0];
            const resizeFileUrl = results[1][0];
            const origFileUrl = results[2][0];

            // Add the URLs to the Database
            return admin
              .firestore()
              .doc(docRef)
              .update({
                [fieldRef + ".original"]: origFileUrl,
                [fieldRef + ".resized"]: resizeFileUrl,
                [fieldRef + ".thumb"]: thumbFileUrl
              });
          })
          .then(() => {
            console.log("Image URLs saved to Firestore.");
            res.status(200).send({
              message: "File Saved."
            });
          })
          .catch(err => {
            console.log("Error:", err);
            res.status(500).send({
              error: err
            });
          });
        });
      busboy.end(req.rawBody);
    } else {
      return res.status(500).json({
        message: "Not Allowed"
      });
    }
  });
};

失败时收到的错误消息是:

  

错误:{ChildProcessError:convert /tmp/ff658860-cc0f-11e8-bd7d-178b6a853dfe.png -thumbnail 100x100> /tmp/thumb_ff658860-cc0f-11e8-bd7d-178b6a853dfe.png在ChildProcess上失败,代码为1。 (/user_code/node_modules/child-process-promise/lib/index.js:132:23)在也许关闭(内部)在ChildProcess.emit(events.js:191:7)上的generateTwo(events.js:106:13) /child_process.js:920:16)。 (internal / child_process.js:351:11)在Epi.One(events.js:96:13)在Socket.emit(events.js:188:7)在Pipe._handle.close [作为_onclose](net.js: 509:12)名称:“ ChildProcessError”,代码:1,childProcess:ChildProcess {域:域{域:null,_events:[Object],_eventsCount:1,_maxListeners:未定义,成员:[]},_events:{错误:[功能:t],关闭:[功能]},_eventsCount:2,_maxListeners:未定义,_closes需要:3,_closesGot:3,已连接:false,signalCode:null,exitCode:1,已杀死:false,spawnfile:'convert ',_handle:null,spawnargs:['convert','/tmp/ff658860-cc0f-11e8-bd7d-178b6a853dfe.png','-thumbnail','100x100>','/ tmp / thumb_ff658860-cc0f-11e8- bd7d-178b6a853dfe.png'],pid:12,标准输入:Socket {连接:false,_hadError:false,_handle:null,_parent:null,_host:null,_可读状态:[Object],可读性:false,域:[Object ],_ events:[Object],_ eventsCount:2,_maxListeners:未定义,_writableState:[Object],可写:false,allowHalfOpen:false ,已销毁:true,_bytes已调度:0,_sockname:null,_pendingData:null,_pendingEncoding:“”,服务器:null,_server:null,写入:[Function:writeAfterFIN],_ idleNext:null,_idlePrev:null,_idleTimeout:-1 },stdout:套接字{连接:false,_hadError:false,_handle:null,_parent:null,_host:null,_visibleState:[Object],可读性:false,域:[Object],_events:[Object],_eventsCount: 4,_maxListeners:未定义,_writableState:[对象],可写:false,allowHalfOpen:false,销毁:true,_bytesDispatched:0,_sockname:null,_pendingData:null,_pendingEncoding:'',服务器:null,_server:null,读取:[Function],_using:true,_idleNext:null,_idlePrev:null,_idleTimeout:-1,写:[Function:writeAfterFIN]},stderr:Socket {连接:false,_hadError:false,_handle:null,_parent:null ,_host:null,_visibleState:[Object],可读:false,域:[Object],_events:[Object],_eventsCount:4,_maxListeners:未定义,_writableState:[Object],wri表:false,allowHalfOpen:false,销毁:true,_bytes调度:0,_sockname:null,_pendingData:null,_pendingEncoding:'',服务器:null,_server:null,读取:[功能],_using:true,_idleNext:null ,_idlePrev:null,_idleTimeout:-1,写入:[Function:writeAfterFIN]},stdio:[[Object],[Object],[Object]]},stdout:”,stderr:'convert:不正确的图片标题{ {1}} / tmp / thumb_ff658860-cc0f-11e8-bd7d-178b6a853dfe.png \'@ error / convert.c / ConvertImageCommand / 3210。\ n'}

我对busboy并不熟悉,但是我相信我正确地结束了该过程,但是由于这通常仅适用于保存云功能后的首次上传,因此我怀疑代码可能不会干净地结束吗?

任何帮助将不胜感激。谢谢。

0 个答案:

没有答案