Я пытаюсь добиться следующего, однако, как новичок, я не вижу, что я делаю неправильно:
- Загрузка файла запускает облачную функцию (https.onRequest())
- Изменение размера изображения 3 раза (большой палец 100x100, большой палец 500x500, изменение размера оригинала, чтобы ограничить его размер)
- Сохранение всех 3 изображений в хранилище Firebase (Google Storage)
- Получить соответствующие подписанные URL-адреса из 3 изображений
- Сохранить подписанные 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'}
Я не знаком с сотрудником, ноЯ полагаю, что я правильно завершаю процесс, но так как это обычно работает только для первой загрузки после сохранения функции облака, я подозреваю, что код может не завершаться чисто?
Любая помощь очень ценится.Спасибо.