57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
// composables/useFileHash.ts
|
|
import { ref } from 'vue'
|
|
|
|
export const useFileHash = () => {
|
|
const isCalculating = ref(false)
|
|
const error = ref<Error | null>(null)
|
|
|
|
const calculateHash = async (file: File): Promise<string> => {
|
|
try {
|
|
if (!process.client) {
|
|
throw new Error('This function can only run in browser')
|
|
}
|
|
|
|
if (!window.crypto || !window.crypto.subtle) {
|
|
throw new Error('Crypto API is not supported in this browser')
|
|
}
|
|
|
|
isCalculating.value = true
|
|
|
|
return await new Promise((resolve, reject) => {
|
|
const reader = new FileReader()
|
|
reader.readAsArrayBuffer(file)
|
|
|
|
reader.onload = async (event) => {
|
|
try {
|
|
const buffer = event.target?.result as ArrayBuffer
|
|
if (!buffer) {
|
|
throw new Error('Failed to read file buffer')
|
|
}
|
|
|
|
const hashBuffer = await window.crypto.subtle.digest('SHA-256', buffer)
|
|
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
|
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
|
resolve(hashHex)
|
|
} catch (err) {
|
|
reject(err)
|
|
}
|
|
}
|
|
|
|
reader.onerror = () => {
|
|
reject(new Error('Failed to read file'))
|
|
}
|
|
})
|
|
} catch (err) {
|
|
error.value = err as Error
|
|
throw err
|
|
} finally {
|
|
isCalculating.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
calculateHash,
|
|
isCalculating,
|
|
error
|
|
}
|
|
} |