forked from StellarSplit/StellarSplit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathimageCompression.ts
More file actions
128 lines (109 loc) · 3.22 KB
/
Copy pathimageCompression.ts
File metadata and controls
128 lines (109 loc) · 3.22 KB
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
/**
* Image compression utility for receipt photos
* Uses Canvas API to compress images while maintaining quality
*/
export interface CompressionOptions {
maxWidth?: number;
maxHeight?: number;
quality?: number; // 0-1, default 0.8
}
const DEFAULT_MAX_WIDTH = 1920;
const DEFAULT_MAX_HEIGHT = 1440;
const DEFAULT_QUALITY = 0.8;
/**
* Compress an image file using Canvas API
* @param file - Image file to compress
* @param options - Compression options
* @returns Promise<Blob> - Compressed image blob
*/
export const compressImage = async (
file: File,
options: CompressionOptions = {}
): Promise<Blob> => {
return new Promise((resolve, reject) => {
const {
maxWidth = DEFAULT_MAX_WIDTH,
maxHeight = DEFAULT_MAX_HEIGHT,
quality = DEFAULT_QUALITY,
} = options;
const reader = new FileReader();
reader.onload = (event) => {
try {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
let width = img.width;
let height = img.height;
// Calculate new dimensions maintaining aspect ratio
if (width > maxWidth) {
height = Math.round((height * maxWidth) / width);
width = maxWidth;
}
if (height > maxHeight) {
width = Math.round((width * maxHeight) / height);
height = maxHeight;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('Failed to get canvas context'));
return;
}
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob);
} else {
reject(new Error('Failed to compress image'));
}
},
'image/jpeg',
quality
);
};
img.onerror = () => {
reject(new Error('Failed to load image'));
};
img.src = event.target?.result as string;
} catch (error) {
reject(error);
}
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsDataURL(file);
});
};
/**
* Convert blob to File object
* @param blob - Blob to convert
* @param fileName - Name for the file
* @returns File object
*/
export const blobToFile = (blob: Blob, fileName: string): File => {
return new File([blob], fileName, { type: 'image/jpeg' });
};
/**
* Get file size in readable format
* @param bytes - Number of bytes
* @returns Formatted file size string
*/
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
};
/**
* Validate if file is a supported image type
* @param file - File to validate
* @returns boolean
*/
export const isValidImageType = (file: File): boolean => {
const validTypes = ['image/jpeg', 'image/png', 'image/webp'];
return validTypes.includes(file.type);
};