有没有办法安排删除 Firebase 存储上的文件?
Is there a way to schedule a deletion of a file on Firebase Storage?
我正在尝试构建一个允许用户上传和下载一些文件的 ReactJS 应用程序。我的应用程序中已经具有 Firebase 存储上传和下载功能。问题是我想安排一个任务,例如每次用户上传他的文件时,它会在 10 分钟内删除一个文件。
有什么建议吗?
Firebase 中没有为此内置任何内容,但 Cloud Storage(底层存储机制)有一种称为 object lifecycle management 的东西,其中:
you can assign a lifecycle management configuration to a bucket. The configuration contains a set of rules which apply to current and future objects in the bucket. When an object meets the criteria of one of the rules, Cloud Storage automatically performs a specified action on the object.
如果删除规则真的像你说的那么稳定,那我就去看看。
如果规则更复杂,我通常会从 Cloud Functions 开始,因为我可以编写 code that run periodically 来进行清理。
如果您正在为此寻找可靠的服务器端解决方案,您可以使用 Scheduled Cloud Function。
这是基于节点的云函数的实现:
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
admin.initializeApp();
/**
* Scheduled Cloud Function that deletes files older than 10 minutes,
* checking every 5 minutes.
*
* Files must be prefixed with "tmpUserUploads" to be checked by this
* function. If a file has a temporary or event-based hold on it, it
* will be skipped over by this function.
*
* @author samthecodingman [MIT License]
* @see
*
* @example
* "tmpUserUploads/someFile.png" // -> checked
* "tmpUserUploads/users/someUserId/someFile.png" // -> checked
* "profilePictures/someUserId/profile@1x.png" // -> not checked
*/
export cleanupTemporaryFiles =
functions.pubsub.schedule('every 5 minutes').onRun(async (context) => {
// variables for tracking statistics
let processedCount = 0, disposedCount = 0, skippedCount = 0, erroredCount = 0, totalCount = 0;
const errorCountsByCode = {};
try {
const bucket = admin.storage().bucket();
// Query for all files starting with "tmpUserUploads"
const [filesArray] = await bucket.getFiles({
prefix: "tmpUserUploads"
});
totalCount = filesArray.length;
// variables with our settings to be reused below
const TIMESTAMP_TEN_MINUTES_AGO = Date.now() - 600000;
const DELETE_OPTIONS = { ignoreNotFound: true };
// If this number is regularly large, consider shortening the interval above
console.log("Found ${totalCount} files that need to be checked.");
// Process purge of each file as applicable keeping track of the results
const deleteOldFileResults = await Promise.all(
filesArray.map(async (file) => {
let metadata;
try {
// get the metadata for this file object
[metadata] = await file.getMetadata();
// pull out the bits we need
const { temporaryHold, eventBasedHold, timeCreated } = metadata;
// held files should not be deleted (will throw an error if you try)
const activeHold = temporaryHold || eventBasedHold;
// dispose when not held and older than 10 minutes
const dispose = !activeHold && timeCreated < TIMESTAMP_TEN_MINUTES_AGO;
if (dispose) {
await file.delete(DELETE_OPTIONS);
disposedCount++;
} else if (activeHold) {
skippedCount++;
}
processedCount++;
return { file, metadata, disposed: dispose, skipped: activeHold };
} catch (error) {
// trap the error so other files still attempt to be deleted
erroredCount++;
processedCount++;
const code = deleteResult.error.code || "unknown";
const errorCountForCode = (errorsByCode[code] || 0) + 1;
// Consider crashing function if same error code is encountered many times
// if (errorCountForCode > 10) {
// throw new Error(`Error code "${code}" has been encountered more than 10 times`);
// }
errorCountsByCode[code] = errorCountForCode;
return { file, metadata, disposed: false, skipped: true, error };
}
})
);
// Assemble an informative log message
const skippedLogMessage = skippedCount === 0
? "No files had active holds"
: `${skippedCount} of these were skipped due to active holds`;
const errorLogMessage = erroredCount === 0
? "no errors were encountered"
: `${erroredCount} were skipped due to errors (Error code breakdown: ${JSON.stringify(errorCountsByCode)})`;
console.log(`${disposedCount}/${totalCount} temporary files were purged. ${skippedLogMessage} and ${errorLogMessage}.`);
} catch (error) {
const stats = JSON.stringify({
disposed: disposedCount,
skipped: skippedCount,
errored: erroredCount,
errorCodeCounts: errorCountsByCode
});
console.error(`Critical failure: ${error.message}. Encounted after processing ${processedCount}/${totalCount} files: ${stats}`);
}
})
我正在尝试构建一个允许用户上传和下载一些文件的 ReactJS 应用程序。我的应用程序中已经具有 Firebase 存储上传和下载功能。问题是我想安排一个任务,例如每次用户上传他的文件时,它会在 10 分钟内删除一个文件。
有什么建议吗?
Firebase 中没有为此内置任何内容,但 Cloud Storage(底层存储机制)有一种称为 object lifecycle management 的东西,其中:
you can assign a lifecycle management configuration to a bucket. The configuration contains a set of rules which apply to current and future objects in the bucket. When an object meets the criteria of one of the rules, Cloud Storage automatically performs a specified action on the object.
如果删除规则真的像你说的那么稳定,那我就去看看。
如果规则更复杂,我通常会从 Cloud Functions 开始,因为我可以编写 code that run periodically 来进行清理。
如果您正在为此寻找可靠的服务器端解决方案,您可以使用 Scheduled Cloud Function。
这是基于节点的云函数的实现:
import * as admin from "firebase-admin";
import * as functions from "firebase-functions";
admin.initializeApp();
/**
* Scheduled Cloud Function that deletes files older than 10 minutes,
* checking every 5 minutes.
*
* Files must be prefixed with "tmpUserUploads" to be checked by this
* function. If a file has a temporary or event-based hold on it, it
* will be skipped over by this function.
*
* @author samthecodingman [MIT License]
* @see
*
* @example
* "tmpUserUploads/someFile.png" // -> checked
* "tmpUserUploads/users/someUserId/someFile.png" // -> checked
* "profilePictures/someUserId/profile@1x.png" // -> not checked
*/
export cleanupTemporaryFiles =
functions.pubsub.schedule('every 5 minutes').onRun(async (context) => {
// variables for tracking statistics
let processedCount = 0, disposedCount = 0, skippedCount = 0, erroredCount = 0, totalCount = 0;
const errorCountsByCode = {};
try {
const bucket = admin.storage().bucket();
// Query for all files starting with "tmpUserUploads"
const [filesArray] = await bucket.getFiles({
prefix: "tmpUserUploads"
});
totalCount = filesArray.length;
// variables with our settings to be reused below
const TIMESTAMP_TEN_MINUTES_AGO = Date.now() - 600000;
const DELETE_OPTIONS = { ignoreNotFound: true };
// If this number is regularly large, consider shortening the interval above
console.log("Found ${totalCount} files that need to be checked.");
// Process purge of each file as applicable keeping track of the results
const deleteOldFileResults = await Promise.all(
filesArray.map(async (file) => {
let metadata;
try {
// get the metadata for this file object
[metadata] = await file.getMetadata();
// pull out the bits we need
const { temporaryHold, eventBasedHold, timeCreated } = metadata;
// held files should not be deleted (will throw an error if you try)
const activeHold = temporaryHold || eventBasedHold;
// dispose when not held and older than 10 minutes
const dispose = !activeHold && timeCreated < TIMESTAMP_TEN_MINUTES_AGO;
if (dispose) {
await file.delete(DELETE_OPTIONS);
disposedCount++;
} else if (activeHold) {
skippedCount++;
}
processedCount++;
return { file, metadata, disposed: dispose, skipped: activeHold };
} catch (error) {
// trap the error so other files still attempt to be deleted
erroredCount++;
processedCount++;
const code = deleteResult.error.code || "unknown";
const errorCountForCode = (errorsByCode[code] || 0) + 1;
// Consider crashing function if same error code is encountered many times
// if (errorCountForCode > 10) {
// throw new Error(`Error code "${code}" has been encountered more than 10 times`);
// }
errorCountsByCode[code] = errorCountForCode;
return { file, metadata, disposed: false, skipped: true, error };
}
})
);
// Assemble an informative log message
const skippedLogMessage = skippedCount === 0
? "No files had active holds"
: `${skippedCount} of these were skipped due to active holds`;
const errorLogMessage = erroredCount === 0
? "no errors were encountered"
: `${erroredCount} were skipped due to errors (Error code breakdown: ${JSON.stringify(errorCountsByCode)})`;
console.log(`${disposedCount}/${totalCount} temporary files were purged. ${skippedLogMessage} and ${errorLogMessage}.`);
} catch (error) {
const stats = JSON.stringify({
disposed: disposedCount,
skipped: skippedCount,
errored: erroredCount,
errorCodeCounts: errorCountsByCode
});
console.error(`Critical failure: ${error.message}. Encounted after processing ${processedCount}/${totalCount} files: ${stats}`);
}
})