Я пытаюсь обновить документ, когда размер группы изображений изменяется, просто чтобы показать прогресс и отслеживать, не удалось ли преобразовать какие-либо изображения.
у нас есть несколько изображений в каталоге, где dir — это идентификатор документа. используя идентификатор документа, мы должны обновить этот документ. мы должны установить поле статуса, статус содержит объекты для преобразованной или не удалось преобразовать информацию. {изображение: путь, обработка: строка}
это будет выглядеть как { ... положение дел : [ имя_файла1:{...}, имя_файла2:{...} ] }
Но когда я загружаю от 5 до 10 изображений, в поле статуса устанавливается только информация о первых 4-5 изображениях. он приостанавливается (или выглядит как пауза или перезаписывается небольшая информация) перед записью последней информации об изображении в статус.
Хотя я пытался просто заменить старые данные новыми в статусе, это означает, что здесь будет только один объект и он будет изменен соответствующим образом. и это работает большую часть времени. я видел редкий случай, когда это тоже не сработало, но в большинстве случаев это работает.
Также пытался создать массив объектов, но при этом также пропускалось несколько записей.
async function updateDoc({filePath, metadata}) {
const [docId, fileName] = filePath.split('/');
const db = admin.firestore();
const collectionRef = db.collection(COLLECTION_PACKS).doc(docId);
const stickersRef = collectionRef.collection(COLLECTION_STICKERS);
let attempts = 0;
const maxAttempts = 5;
while (attempts < maxAttempts) {
try {
await db.runTransaction(async (transaction) => {
const updatedStatus = {};
updatedStatus[fileName] = metadata;
// Update the document with the new status and progress
transaction.update(collectionRef, {
status: updatedStatus,
});
});
console.info("Updated document for", filePath);
break; // Exit the loop if successful
} catch (error) {
attempts++;
console.error(`Error updating document (attempt ${attempts}):`, error);
if (attempts >= maxAttempts) {
throw error; // Re-throw the error after max attempts
}
await new Promise(resolve => setTimeout(resolve, 1000 * attempts)); // Exponential backoff
}
}
}
Я также попробовал этот простой фрагмент, который добавляет новый объект в массив статусов, но это также не позволило сохранить интеграцию данных.
const db = admin.firestore();
const docRef = db.collection(COLLECTION_PACKS).doc(docId);
await db.runTransaction(async (transaction) => {
const docSnapshot = await transaction.get(docRef);
if (docSnapshot.exists) {
docRef.update({
status: FieldValue.arrayUnion(metadata),
});
}
});
Будь то вложенные объекты или массив объектов, любой из них приемлем.
Обновлено: Я наблюдал, как данные перезаписываются немногие объекты вставляются раньше и позже быстро заменяются новыми объектами. на несколько секунд это выглядит так
status : [
object1, object2
]
тогда это становится
status : [
object3, object4, object5
]
объект, который быстро вставляется при ранних триггерах, заменяется позже, и кажется, что новое триггерное событие не знает об этих вставленных данных. это расовая проблема.
Дополнительная деталь кода: вот как вызывается метод функции триггера события updateDoc()
exports.resize = onObjectFinalized({
bucket:'sticker-app-2ad48',
region:'us-central1',
timeoutSeconds: 450,
memory:'1GiB',
cpu:2
},
transform
)
async function transform(event){
...
let metadatainfo = await bucket.file(filePath).getMetadata()[0];
// Early return;
if (metadatainfo?.metadata?.process === "done"){
console.info("previously done processing | ",fileName)
return;
}
try {
if (valid){
console.info("image is valid ✅ |",filePath)
await updateMeta({filePath, valid:true, process:"done"});
return;
}
... // invoke image processing with sharpjs
if (buffer?.length > 0){
// save
log("✔️ transformed ", fileName)
await bucket.file(`${filePath}`).save(buffer);
await updateMeta({filePath, valid:true, process:"done"});
}else{
// leave image data as it is. set metadata
log("❌ failed to transform ", fileName)
await updateMeta({filePath, valid:false, process:"done"});
}
buffer = null;
} catch (e) {
console.error('Error_> ', e)
await updateMeta({filePath, valid:false, process:"done", error: e.message});
}
}
async function updateMeta({filePath, process, valid, error}){
// console.info("updateMeta ",filePath)
let metadata = { process, valid }
if (error) { metadata.error = error; }
await bucket.file(filePath).setMetadata({
metadata: metadata
})
await updateDoc({ filePath, metadata})
}
Обновлено: добавление кода на стороне клиента, который может быть причиной проблемы. Код клиентской стороны:
const promises = selectedFiles.map(..) => fetch(`${baseUrl}/upload`, ...) )
// Uploads images which triggers the onObjectFinalized
// to prcoess and add the info to the doc
Promise.all(promises)
.then((responses) => {
const jsonPromises = responses.map((response) => response.json());
return Promise.all(jsonPromises);
})
.then(uploadedImages=>{
// structure the stickers objects with download url
...
})
.then(stickerObjects=>{
...
// Populate subcollection "stickers"
return fetch(`${baseUrl}/addStickerPack/${docId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: body
})
})
.then(result=>{
console.info("Populated subcollection 'stickers' of the Doc ", {docId})
......
})
.catch(...)
.finally(...)
последний блок «тогда», который печатает «заполненную подколлекцию...», помог мне найти основную причину. Когда эта строка печатает поле статуса документа, для которого установлено значение неопределенное, что стирает поле статуса, и после этого журнала все, что добавляется в статус, остается интегрированным без каких-либо проблем. удаление метода выборки предотвращает любые изменения в подколлекции документов, устраняет проблему потери данных. Не знаю, почему это происходит.
вот внутренний код, который создает и обновляет подколлекцию.
app.put('/addStickerPack/:stickerPackId', async (req, res) => {
try {
const stickerPackDocId = req.params.stickerPackId;
const {
...
name,
publisher,
stickers
} = req.body;
const stickerPackData = {
name,
publisher,
...
};
const stickerPackRef = admin.firestore().collection(COLLECTION_PACKS).doc(stickerPackDocId);
await stickerPackRef.set(stickerPackData);
// Create sticker documents within the pack
const stickerPromises = stickers.map(async (sticker) => {
const stickerRef = stickerPackRef.collection(COLLECTION_STICKERS).doc();
const stickerData = {
name: sticker.name, // Replace with actual image storage logic
image_file: sticker.image_file, // Replace with actual image storage logic
emojis: sticker.emojis,
filePath: sticker.filePath,
valid:false,
};
console.info(sticker.image_file)
return stickerRef.set(stickerData);
});
// Wait for all sticker writes to complete
await Promise.all(stickerPromises);
console.info('Sticker pack added successfully:', stickerPackData);
const stickerPackId = stickerPackRef.id;
res.status(201).send({message:'Sticker pack added successfully', id:stickerPackId});
} catch (error) {
console.error('Error adding sticker pack:', error);
res.status(500).send('Internal Server Error');
}
});
🤔 А знаете ли вы, что...
JavaScript может выполняться как на стороне клиента (в браузере), так и на стороне сервера (с использованием Node.js).
Использование db.runTransaction()
не требуется, поскольку вы не получаете более старых значений для использования в обновлении, поэтому это просто добавляет больше ненужной обработки.
Если ваша структура данных на самом деле { ... status : { filename1:{...}, filename2:{...} } }
(обратите внимание, что я заменил квадратные скобки фигурными скобками, я полагаю, вы имели в виду объект, а не массив), то вы можете просто обновить вложенный путь без использования транзакции. Повторные попытки также не требуются:
// this is a docRef, not a colRef as in your example
const docRef = db.collection(COLLECTION_PACKS).doc(docId);
const update = {
[`status.${fileName}`]: metadata,
};
await docRef.update(update);
===== Обновлено:
Просто чтобы объяснить дальше, когда вы говорите:
Но когда я загружаю от 5 до 10 изображений, в поле статуса устанавливается только информация о первых 4-5 изображениях. он делает паузу перед записью информации о последнем изображении в статус.
Я не думаю, что это остановится. Я считаю, что у вас состояние гонки. Обратите внимание, что вы перезаписываете весь объект status
в каждом файле. Это означает, что если файлы запускаются в этом порядке 1-2-3-4, но завершают обработку 1-2-4-3, вы получите информацию, которая была у вас в третьем файле, а не в последнем.
Это не соответствует коду, который вы показываете, но, судя по тому, что вы говорите, это возможно, если вы намеревались получить весь объект status
перед обновлением.
===== Обновлено еще раз:
Что касается последнего добавленного вами кода, который используется на стороне клиента: этот код перезаписывает весь документ, в результате чего свойство status
становится неопределенным. Вот почему вы добавляете 1,2,очистить от клиента,3,4,5 и в итоге получаете только 3,4,5.
Измените строку, которая устанавливает документ, очищая остальную часть документа. Вместо:
await stickerPackRef.set(stickerPackData);
скорее используйте
await stickerPackRef.set(stickerPackData, {merge:true});
OR
await stickerPackRef.update(stickerPackData);