From 16e8aca7141199dd67ee619daf7af991591909ee Mon Sep 17 00:00:00 2001 From: mroxso <24775431+mroxso@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:14:06 +0100 Subject: [PATCH] rm exif data from images before uploading (#55) --- components/UploadComponent.tsx | 88 +++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/components/UploadComponent.tsx b/components/UploadComponent.tsx index 653e84b..cfdf57b 100644 --- a/components/UploadComponent.tsx +++ b/components/UploadComponent.tsx @@ -48,6 +48,57 @@ async function calculateBlurhash(file: File): Promise { }) } +/** + * Removes EXIF metadata from an image by redrawing it on a canvas + * @param file The image file to process + * @returns A Promise that resolves to a new File object without EXIF data + */ +async function removeExifData(file: File): Promise { + return new Promise((resolve, reject) => { + // Only process images + if (!file.type.startsWith("image/")) { + resolve(file) + return + } + + const img = new Image() + img.onload = () => { + // Create a canvas with the same dimensions as the image + const canvas = document.createElement("canvas") + canvas.width = img.width + canvas.height = img.height + + // Draw the image to the canvas, which strips EXIF data + const ctx = canvas.getContext("2d") + if (!ctx) { + reject(new Error("Could not get canvas context")) + return + } + + ctx.drawImage(img, 0, 0) + + // Convert canvas to blob with the same type as the original file + canvas.toBlob((blob) => { + if (!blob) { + reject(new Error("Could not create blob from canvas")) + return + } + + // Create a new File object with the same name and type + const sanitizedFile = new File([blob], file.name, { + type: file.type, + lastModified: file.lastModified + }) + + resolve(sanitizedFile) + }, file.type) + } + + img.onerror = () => reject(new Error("Failed to load image")) + img.src = URL.createObjectURL(file) + }) +} + const UploadComponent: React.FC = () => { const { publish } = useNostr() const { createHash } = require("crypto") @@ -102,15 +153,16 @@ const UploadComponent: React.FC = () => { setShouldFetch(true) }, []) - const handleFileChange = (event: ChangeEvent) => { + const handleFileChange = async (event: ChangeEvent) => { const file = event.target.files?.[0] if (file) { + // Create a clean URL for preview that will be revoked later const url = URL.createObjectURL(file) if (url.startsWith("blob:")) { setPreviewUrl(url) } - // Optional: Bereinigung alter URLs + // Optional: Cleanup old URLs return () => URL.revokeObjectURL(url) } } @@ -125,7 +177,7 @@ const UploadComponent: React.FC = () => { const formData = new FormData(event.currentTarget) const desc = formData.get("description") as string - const file = formData.get("file") as File + let file = formData.get("file") as File let sha256 = "" let finalNoteContent = desc let finalFileUrl = "" @@ -145,16 +197,27 @@ const UploadComponent: React.FC = () => { // If file is present, upload it to the media server if (file) { - const readFileAsArrayBuffer = (file: File): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader() - reader.onload = () => resolve(reader.result as ArrayBuffer) - reader.onerror = () => reject(reader.error) - reader.readAsArrayBuffer(file) - }) - } - try { + // Remove EXIF data from image if it's an image file + if (file.type.startsWith("image/")) { + try { + file = await removeExifData(file) + console.log("EXIF data removed from image") + } catch (error) { + console.error("Error removing EXIF data:", error) + // Continue with original file if EXIF removal fails + } + } + + const readFileAsArrayBuffer = (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => resolve(reader.result as ArrayBuffer) + reader.onerror = () => reject(reader.error) + reader.readAsArrayBuffer(file) + }) + } + const arrayBuffer = await readFileAsArrayBuffer(file) const hashBuffer = createHash("sha256").update(Buffer.from(arrayBuffer)).digest() sha256 = hashBuffer.toString("hex") @@ -256,6 +319,7 @@ const UploadComponent: React.FC = () => { console.log("final Event: ") console.log(signedEvent) publish(signedEvent) + // alert("OK:" + signedEvent) } setIsLoading(false)