shrink imagies
This commit is contained in:
parent
8457604da7
commit
a6022d31c6
9 changed files with 131 additions and 39 deletions
|
@ -42,7 +42,7 @@ export default function HomePage({ postsService, mediaService }: HomePageProps)
|
|||
try {
|
||||
const media = await Promise.all(
|
||||
files.map(async ({ file, width, height }) => {
|
||||
const { mediaId, url } = await mediaService.uploadFile(file)
|
||||
const { mediaId, url } = await mediaService.uploadImage(file)
|
||||
|
||||
return {
|
||||
mediaId,
|
||||
|
|
|
@ -2,7 +2,7 @@ import { ApiClient } from '../api/client.ts'
|
|||
|
||||
export class MediaService {
|
||||
constructor(private readonly client: ApiClient) {}
|
||||
async uploadFile(file: File): Promise<{ mediaId: string; url: URL }> {
|
||||
async uploadImage(file: File): Promise<{ mediaId: string; url: URL }> {
|
||||
const body = new FormData()
|
||||
body.append('file', file)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import { useState } from 'react'
|
|||
import FancyTextEditor, { TextInputKeyDownEvent } from './inputs/FancyTextEditor.tsx'
|
||||
import Button from './buttons/Button.tsx'
|
||||
import { openFileDialog } from '../utils/openFileDialog.ts'
|
||||
import makePica from 'pica'
|
||||
|
||||
interface NewPostWidgetProps {
|
||||
onSubmit: (
|
||||
|
@ -121,11 +122,13 @@ async function createAttachment(file: File): Promise<Attachment> {
|
|||
throw new Error('not an image')
|
||||
}
|
||||
|
||||
file = await optimizeImageSize(file)
|
||||
|
||||
const objectUrl = URL.createObjectURL(file)
|
||||
const { width, height } = await getImageFileDimensions(objectUrl)
|
||||
|
||||
return {
|
||||
id: crypto.randomUUID(),
|
||||
id: getRandomId(),
|
||||
file,
|
||||
objectUrl,
|
||||
width,
|
||||
|
@ -144,3 +147,82 @@ function getImageFileDimensions(objectURL: string): Promise<{ width: number; hei
|
|||
img.src = objectURL
|
||||
})
|
||||
}
|
||||
|
||||
const pica = makePica()
|
||||
|
||||
async function optimizeImageSize(
|
||||
file: File,
|
||||
{
|
||||
targetMaxWidth = 1920,
|
||||
targetMaxHeight = 1080,
|
||||
targetSizeBytes = 500 * 1024,
|
||||
outputType = 'image/jpeg',
|
||||
quality = 0.9,
|
||||
}: {
|
||||
targetMaxWidth?: number
|
||||
targetMaxHeight?: number
|
||||
targetSizeBytes?: number
|
||||
outputType?: string
|
||||
quality?: number
|
||||
} = {},
|
||||
): Promise<File> {
|
||||
const img = document.createElement('img')
|
||||
const url = URL.createObjectURL(file)
|
||||
img.src = url
|
||||
|
||||
await img.decode()
|
||||
|
||||
console.debug('processing image', {
|
||||
width: img.width,
|
||||
height: img.height,
|
||||
targetMaxWidth,
|
||||
targetMaxHeight,
|
||||
targetSizeBytes,
|
||||
outputType,
|
||||
quality,
|
||||
})
|
||||
|
||||
const scale = Math.min(1, targetMaxWidth / img.width, targetMaxHeight / img.height)
|
||||
const width = Math.floor(img.width * scale)
|
||||
const height = Math.floor(img.height * scale)
|
||||
|
||||
const srcCanvas = document.createElement('canvas')
|
||||
srcCanvas.width = img.width
|
||||
srcCanvas.height = img.height
|
||||
srcCanvas.getContext('2d')!.drawImage(img, 0, 0)
|
||||
|
||||
const dstCanvas = document.createElement('canvas')
|
||||
dstCanvas.width = width
|
||||
dstCanvas.height = height
|
||||
|
||||
await pica.resize(srcCanvas, dstCanvas)
|
||||
|
||||
let blob = await pica.toBlob(dstCanvas, outputType, quality)
|
||||
|
||||
while (blob.size > targetSizeBytes && quality > 0.1) {
|
||||
quality = parseFloat((quality - 0.1).toFixed(2))
|
||||
blob = await pica.toBlob(dstCanvas, outputType, quality)
|
||||
}
|
||||
|
||||
URL.revokeObjectURL(url)
|
||||
|
||||
return new File([blob], file.name, { type: file.type })
|
||||
}
|
||||
|
||||
function getRandomId() {
|
||||
if (window.isSecureContext) {
|
||||
return crypto.randomUUID()
|
||||
}
|
||||
|
||||
// Fallback using getRandomValues
|
||||
const bytes = new Uint8Array(16)
|
||||
crypto.getRandomValues(bytes)
|
||||
|
||||
// Format according to RFC4122 version 4
|
||||
bytes[6] = (bytes[6]! & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8]! & 0x3f) | 0x80
|
||||
|
||||
const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0'))
|
||||
|
||||
return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue