Загрузка изображений Firebase, изменение размера с помощью облачных функций (https) и сохранение в Firestore - PullRequest
0 голосов
/ 10 октября 2018

Я пытаюсь добиться следующего, однако, как новичок, я не вижу, что я делаю неправильно:

  1. Загрузка файла запускает облачную функцию (https.onRequest())
  2. Изменение размера изображения 3 раза (большой палец 100x100, большой палец 500x500, изменение размера оригинала, чтобы ограничить его размер)
  3. Сохранение всех 3 изображений в хранилище Firebase (Google Storage)
  4. Получить соответствующие подписанные URL-адреса из 3 изображений
  5. Сохранить подписанные URL-адреса в Firestore

Следующий код работает, но не согласованно:

В моем индексеФайл .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 не удалось с кодом 1 в ChildProcess.(/user_code/node_modules/child-process-promise/lib/index.js:132:23) в emitTwo (events.js: 106: 13) в ChildProcess.emit (events.js: 191: 7) в MaybeClose (внутренний/child_process.js:920:16) в Socket.(internal / child_process.js: 351: 11) в emitOne (events.js: 96: 13) в Socket.emit (events.js: 188: 7) в Pipe._handle.close [as _onclose] (net.js:509: 12) name: 'ChildProcessError', код: 1, childProcess: ChildProcess {domain: Domain {domain: null, _events: [Object], _eventsCount: 1, _maxListeners: undefined, члены: []}, _events: {error: [Функция: t], закрыть: [Функция]}, _eventsCount: 2, _maxListeners: undefined, _closesNeeded: 3, _closesGot: 3, подключено: false, signalCode: null, exitCode: 1, kill: 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, stdin: Socket {connection: false, _hadError: false, _handle: null, _parent: null, _host: null, _readableState: [Object], readable: false, domain: [Object], _events: [Object], _eventsCount: 2, _maxListeners: undefined, _writableState: [Object], доступная для записи: false, allowHalfOpen: false, уничтожено: true, _bytesDispatched: 0, _sockname: null, _pendingData: null, _pendingEncoding: '', сервер: null, _server: null, запись: [Функция: writeAfterFIN], _idleNext: null, _idlePrev: null, _idleTimeout: -1}, stdout: Socket {connection: false, _hadError: false, _handle: null, _parent: null, _host: null, _readableState: [Object], readable: false, domain: [Object], _events: [Object], _eventsCount:4, _maxListeners: undefined, _writableState: [Object], доступный для записи: false, allowHalfOpen: false, destroy: true, _bytesDispatched: 0, _sockname: null, _pendingData: null, _pendingEncoding: '', сервер: null, _server: null, чтение: [Function], _consuming: true, _idleNext: null, _idlePrev: null, _idleTimeout: -1, запись: [Function: writeAfterFIN]}, stderr: Socket {connection: false, _hadError: false, _handle: null, _parent: null, _host: null, _readableState: [Object], readable: false, домен: [Object], _events: [Object], _eventsCount: 4, _maxListeners: undefined, _writableState: [Object], wriтаблица: false, allowHalfOpen: false, уничтожено: true, _bytesDispatched: 0, _sockname: null, _pendingData: null, _pendingEncoding: '', сервер: null, _server: null, чтение: [Функция], _consuming: true, _idleNext: null, _idlePrev: null, _idleTimeout: -1, запись: [Функция: writeAfterFIN]}, stdio: [[Object], [Object], [Object]]}, stdout: '', stderr: 'convert: неправильный заголовок изображения /tmp/ff658860-cc0f-11e8-bd7d-178b6a853dfe.png\' @ error/png.c/ReadPNGImage/3927.\nconvert: no images defined / tmp / thumb_ff658860-cc0f-11e8-bd7d-178b6a853dfe.png \ '@ error / convert.c / ConvertImageCommand / 3210. \ N'}

Я не знаком с сотрудником, ноЯ полагаю, что я правильно завершаю процесс, но так как это обычно работает только для первой загрузки после сохранения функции облака, я подозреваю, что код может не завершаться чисто?

Любая помощь очень ценится.Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...