Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | 1x | import api from './api'
interface PresignResponse {
uploadUrl: string
publicUrl: string
}
// Resize + compress to JPEG in the browser before uploading.
// Caps the longer edge at 1024px and compresses at 80% quality.
async function compressImage(file: File): Promise<File> {
const MAX = 1024
const QUALITY = 0.8
return new Promise((resolve) => {
const img = new Image()
const objectUrl = URL.createObjectURL(file)
img.onload = () => {
URL.revokeObjectURL(objectUrl)
const scale = Math.min(1, MAX / Math.max(img.width, img.height))
const w = Math.round(img.width * scale)
const h = Math.round(img.height * scale)
const canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h
canvas.getContext('2d')!.drawImage(img, 0, 0, w, h)
canvas.toBlob(blob => {
if (!blob) { resolve(file); return }
const name = file.name.replace(/\.[^.]+$/, '.jpg')
resolve(new File([blob], name, { type: 'image/jpeg' }))
}, 'image/jpeg', QUALITY)
}
img.onerror = () => { URL.revokeObjectURL(objectUrl); resolve(file) }
img.src = objectUrl
})
}
export const imageService = {
async upload(file: File): Promise<string> {
const compressed = await compressImage(file)
const params = new URLSearchParams({ filename: compressed.name })
const { uploadUrl, publicUrl } = await api
.post<PresignResponse>(`/images/presign?${params}`)
.then(r => r.data)
await fetch(uploadUrl, {
method: 'PUT',
body: compressed,
headers: { 'Content-Type': 'image/jpeg' },
})
return publicUrl
},
}
|