Zephyrrr
Zephyrrr
CCConvex Community
Created by Zephyrrr on 5/28/2025 in #support-community
How to Get File Size in onUpload with Convex R2 When getMetadata Returns Null?
Hi everyone, I'm working with Convex and the R2 component for file uploads. My goal is to get the size of the uploaded file immediately after the upload completes, specifically within the onUpload callback, so I can track storage usage per user. The issue I'm encountering is that when I call r2.getMetadata(ctx, key) inside the onUpload function, it returns null. I suspect this is because onUpload might be running as part of the syncMetadata process, and the metadata isn't fully available or settled at that exact moment through r2.getMetadata. Here's the relevant part of my r2.clientApi configuration:
export const { generateUploadUrl, syncMetadata } = r2.clientApi({
checkUpload: async (ctx, bucket) => {
const user = await ctx.runQuery(api.users.current);
if (!user) {
throw new ConvexError("unauthorized");
}
// TODO: check if the storage is full
},
onUpload: async (ctx, key) => {
// Problem: metadata is null here
const metadata = await r2.getMetadata(ctx, key);
console.log("Metadata in onUpload:", metadata);

const user = await ctx.runQuery(api.users.current);
if (!user || !metadata) {
return;
}

await totalStorageAggregate.insert(ctx, {
key: user._id,
id: key,
sumValue: metadata.size, // metadata.size would be undefined if metadata is null
});
},
onDelete: async (ctx, key) => {
const user = await ctx.runQuery(api.users.current);
if (!user) {
throw new ConvexError("unauthorized");
}

await totalStorageAggregate.delete(ctx, {
key: user._id,
id: key,
});
},
});
export const { generateUploadUrl, syncMetadata } = r2.clientApi({
checkUpload: async (ctx, bucket) => {
const user = await ctx.runQuery(api.users.current);
if (!user) {
throw new ConvexError("unauthorized");
}
// TODO: check if the storage is full
},
onUpload: async (ctx, key) => {
// Problem: metadata is null here
const metadata = await r2.getMetadata(ctx, key);
console.log("Metadata in onUpload:", metadata);

const user = await ctx.runQuery(api.users.current);
if (!user || !metadata) {
return;
}

await totalStorageAggregate.insert(ctx, {
key: user._id,
id: key,
sumValue: metadata.size, // metadata.size would be undefined if metadata is null
});
},
onDelete: async (ctx, key) => {
const user = await ctx.runQuery(api.users.current);
if (!user) {
throw new ConvexError("unauthorized");
}

await totalStorageAggregate.delete(ctx, {
key: user._id,
id: key,
});
},
});
How can I reliably get the file size (and other metadata) for a newly uploaded file within the Convex backend flow, ideally associated with the onUpload event or shortly thereafter? Any insights, examples, or alternative approaches would be greatly appreciated! Thanks!
27 replies