All files / src/services image.ts

3.22% Statements 1/31
0% Branches 0/2
0% Functions 0/7
3.7% Lines 1/27

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
  },
}