How to upload images using Convex storage? (Solved)
I'm wondering if anyone has played around using Convex storage for their text editors?
I'm using the Novel.sh package, and they are providing an uploadFn like this:
I have stripped it down to get rid of the Vercel blob storage implementation, and though I would use the uploadstuff.dev, but I really struggle to make it work within the onUpload. It does not seem to run the following lines:
If I run the console.log, I get the following:
So it would seem that the
import { createImageUpload } from "novel/plugins"
import { toast } from "sonner"
const onUpload = async (file: File) => {
console.log("Uploading file", file)
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);
await startUpload([file])
}
export const uploadFn = createImageUpload({
onUpload,
validateFn: (file) => {
if (!file.type.includes("image/")) {
toast.error("File type not supported.")
return false
}
if (file.size / 1024 / 1024 > 20) {
toast.error("File size too big (max 20MB).")
return false
}
return true
},
})
import { createImageUpload } from "novel/plugins"
import { toast } from "sonner"
const onUpload = async (file: File) => {
console.log("Uploading file", file)
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);
await startUpload([file])
}
export const uploadFn = createImageUpload({
onUpload,
validateFn: (file) => {
if (!file.type.includes("image/")) {
toast.error("File type not supported.")
return false
}
if (file.size / 1024 / 1024 > 20) {
toast.error("File size too big (max 20MB).")
return false
}
return true
},
})
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);
await startUpload([file])
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);
await startUpload([file])
argument #0:
'Uploading file'
file:
File {
name: 'IMG_1403.CR3',
lastModified: 1701880784000,
lastModifiedDate: new Date('2023-12-06T16:39:44.000Z'),
webkitRelativePath: '',
size: 18311314,
type: 'image/x-canon-cr3'
}
argument #0:
'Uploading file'
file:
File {
name: 'IMG_1403.CR3',
lastModified: 1701880784000,
lastModifiedDate: new Date('2023-12-06T16:39:44.000Z'),
webkitRelativePath: '',
size: 18311314,
type: 'image/x-canon-cr3'
}
onUpload
is running but not the Convex specific functions. What have I missed?1 Reply
I figured it out and added some compression too.
const useUploadFn = () => {
const generateUploadUrl = useMutation(api.files.generateUploadUrl)
const getStorageUrl = useMutation(api.files.getStorageUrl)
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
}
const onUpload = async (file: File) => {
const compressionToastId = toast.loading("Compressing image...")
const compressedFile = await imageCompression(file, options)
toast.dismiss(compressionToastId)
const promise = generateUploadUrl().then((postUrl) => {
return fetch(postUrl, {
method: "POST",
headers: {
"Content-Type": compressedFile?.type || "application/octet-stream",
},
body: compressedFile,
})
})
return new Promise((resolve, reject) => {
toast.promise(
promise.then(async (res) => {
if (res.status === 200) {
const { storageId } = await res.json()
const getUrl = await getStorageUrl({ storageId })
resolve(getUrl)
} else {
throw new Error("Error adding image. Please try again.")
}
}),
{
loading: "Adding image...",
success: "Image added successfully.",
error: (e) => {
reject(e)
return e.message
},
}
)
})
}
const useUploadFn = () => {
const generateUploadUrl = useMutation(api.files.generateUploadUrl)
const getStorageUrl = useMutation(api.files.getStorageUrl)
const options = {
maxSizeMB: 1,
maxWidthOrHeight: 1920,
useWebWorker: true,
}
const onUpload = async (file: File) => {
const compressionToastId = toast.loading("Compressing image...")
const compressedFile = await imageCompression(file, options)
toast.dismiss(compressionToastId)
const promise = generateUploadUrl().then((postUrl) => {
return fetch(postUrl, {
method: "POST",
headers: {
"Content-Type": compressedFile?.type || "application/octet-stream",
},
body: compressedFile,
})
})
return new Promise((resolve, reject) => {
toast.promise(
promise.then(async (res) => {
if (res.status === 200) {
const { storageId } = await res.json()
const getUrl = await getStorageUrl({ storageId })
resolve(getUrl)
} else {
throw new Error("Error adding image. Please try again.")
}
}),
{
loading: "Adding image...",
success: "Image added successfully.",
error: (e) => {
reject(e)
return e.message
},
}
)
})
}