rm exif data from images before uploading (#55)

This commit is contained in:
mroxso
2025-03-20 17:14:06 +01:00
committed by GitHub
parent b956ba0a21
commit 16e8aca714

View File

@@ -48,6 +48,57 @@ async function calculateBlurhash(file: File): Promise<string> {
})
}
/**
* 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<File> {
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<HTMLInputElement>) => {
const handleFileChange = async (event: ChangeEvent<HTMLInputElement>) => {
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<ArrayBuffer> => {
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<ArrayBuffer> => {
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)