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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
import RNFetchBlob from 'rn-fetch-blob'
import ImageResizer from '@bam.tech/react-native-image-resizer'
export interface DownloadAndResizeOpts {
uri: string
width: number
height: number
mode: 'contain' | 'cover' | 'stretch'
maxSize: number
timeout: number
}
export interface Image {
path: string
mime: string
size: number
width: number
height: number
}
export async function downloadAndResize(opts: DownloadAndResizeOpts) {
let appendExt
try {
const urip = new URL(opts.uri)
const ext = urip.pathname.split('.').pop()
if (ext === 'jpg' || ext === 'jpeg') {
appendExt = 'jpeg'
} else if (ext === 'png') {
appendExt = 'png'
} else {
return
}
} catch (e: any) {
console.error('Invalid URI', opts.uri, e)
return
}
let downloadRes
try {
const downloadResPromise = RNFetchBlob.config({
fileCache: true,
appendExt,
}).fetch('GET', opts.uri)
const to1 = setTimeout(() => downloadResPromise.cancel(), opts.timeout)
downloadRes = await downloadResPromise
clearTimeout(to1)
let localUri = downloadRes.path()
if (!localUri.startsWith('file://')) {
localUri = `file://${localUri}`
}
return await resize(localUri, opts)
} finally {
if (downloadRes) {
downloadRes.flush()
}
}
}
export interface ResizeOpts {
width: number
height: number
mode: 'contain' | 'cover' | 'stretch'
maxSize: number
}
export async function resize(
localUri: string,
opts: ResizeOpts,
): Promise<Image> {
for (let i = 0; i < 9; i++) {
const quality = 1.0 - i / 10
const resizeRes = await ImageResizer.createResizedImage(
localUri,
opts.width,
opts.height,
'JPEG',
quality,
undefined,
undefined,
undefined,
{mode: opts.mode},
)
if (resizeRes.size < opts.maxSize) {
return {
path: resizeRes.path,
mime: 'image/jpeg',
size: resizeRes.size,
width: resizeRes.width,
height: resizeRes.height,
}
}
}
throw new Error(
`This image is too big! We couldn't compress it down to ${opts.maxSize} bytes`,
)
}
export async function compressIfNeeded(
img: Image,
maxSize: number,
): Promise<Image> {
const origUri = `file://${img.path}`
if (img.size < maxSize) {
return img
}
return await resize(origUri, {
width: img.width,
height: img.height,
mode: 'stretch',
maxSize,
})
}
export interface Dim {
width: number
height: number
}
export function scaleDownDimensions(dim: Dim, max: Dim): Dim {
if (dim.width < max.width && dim.height < max.height) {
return dim
}
let wScale = dim.width > max.width ? max.width / dim.width : 1
let hScale = dim.height > max.height ? max.height / dim.height : 1
if (wScale < hScale) {
return {width: dim.width * wScale, height: dim.height * wScale}
}
return {width: dim.width * hScale, height: dim.height * hScale}
}
|