0
# Utilities
1
2
Helper functions for image compression, base64 encoding, and upload configuration management.
3
4
## Capabilities
5
6
### Image Compression
7
8
Client-side image compression to reduce file sizes before upload.
9
10
```typescript { .api }
11
/**
12
* Compress an image file on the client side
13
* @param file - Image file to compress
14
* @param options - Compression configuration
15
* @returns Promise with compressed image and metadata
16
*/
17
function compressImage(file: File, options: CompressOptions): Promise<CompressResult>;
18
19
interface CompressOptions {
20
/** Output quality (0-1), defaults to 0.92 */
21
quality?: number;
22
/** Don't compress if result is larger than original */
23
noCompressIfLarger?: boolean;
24
/** Maximum width constraint in pixels */
25
maxWidth?: number;
26
/** Maximum height constraint in pixels */
27
maxHeight?: number;
28
}
29
30
interface CompressResult {
31
/** Compressed image as Blob or original File */
32
dist: Blob | File;
33
/** Final width in pixels */
34
width: number;
35
/** Final height in pixels */
36
height: number;
37
}
38
```
39
40
**Usage Examples:**
41
42
```typescript
43
import { compressImage } from "qiniu-js";
44
45
// Basic compression
46
const fileInput = document.getElementById('imageInput') as HTMLInputElement;
47
const originalFile = fileInput.files[0];
48
49
try {
50
const compressed = await compressImage(originalFile, {
51
quality: 0.8,
52
maxWidth: 1920,
53
maxHeight: 1080,
54
noCompressIfLarger: true
55
});
56
57
console.log(`Original: ${originalFile.size} bytes`);
58
console.log(`Compressed: ${compressed.dist.size} bytes`);
59
console.log(`Dimensions: ${compressed.width}x${compressed.height}`);
60
61
// Use compressed image for upload
62
const subscription = upload(compressed.dist, 'compressed-image.jpg', token);
63
64
} catch (error) {
65
console.error('Compression failed:', error);
66
}
67
68
// Aggressive compression for thumbnails
69
async function createThumbnail(file: File): Promise<Blob> {
70
const result = await compressImage(file, {
71
quality: 0.7,
72
maxWidth: 300,
73
maxHeight: 300
74
});
75
76
return result.dist as Blob;
77
}
78
79
// Quality comparison
80
async function compressByQuality(file: File) {
81
const qualities = [0.5, 0.7, 0.9];
82
83
for (const quality of qualities) {
84
const result = await compressImage(file, { quality });
85
console.log(`Quality ${quality}: ${result.dist.size} bytes`);
86
}
87
}
88
```
89
90
### Base64 Encoding
91
92
URL-safe base64 encoding and decoding functions for Qiniu API compatibility.
93
94
```typescript { .api }
95
/**
96
* Encode data to URL-safe base64 format used by Qiniu APIs
97
* @param v - Data to encode (string or other)
98
* @returns URL-safe base64 encoded string
99
*/
100
function urlSafeBase64Encode(v: any): string;
101
102
/**
103
* Decode URL-safe base64 data
104
* @param v - URL-safe base64 encoded string
105
* @returns Decoded string
106
*/
107
function urlSafeBase64Decode(v: any): string;
108
```
109
110
**Usage Examples:**
111
112
```typescript
113
import { urlSafeBase64Encode, urlSafeBase64Decode } from "qiniu-js";
114
115
// Encode text for watermarks
116
const watermarkText = "© 2024 My Company";
117
const encodedText = urlSafeBase64Encode(watermarkText);
118
console.log('Encoded:', encodedText); // Safe for URLs
119
120
// Encode image URLs for watermarks
121
const logoUrl = "https://example.com/logo.png";
122
const encodedLogoUrl = urlSafeBase64Encode(logoUrl);
123
124
// Use in watermark operations
125
const watermarkUrl = `watermark/1/image/${encodedLogoUrl}/dissolve/80`;
126
127
// Decode data
128
const decodedText = urlSafeBase64Decode(encodedText);
129
console.log('Decoded:', decodedText); // "© 2024 My Company"
130
131
// Handle Chinese and Unicode text
132
const chineseText = "七牛云存储";
133
const encodedChinese = urlSafeBase64Encode(chineseText);
134
const decodedChinese = urlSafeBase64Decode(encodedChinese);
135
console.log('Chinese text preserved:', decodedChinese === chineseText);
136
```
137
138
### HTTP Headers Generation
139
140
Generate properly formatted headers for different types of upload requests.
141
142
```typescript { .api }
143
/**
144
* Generate headers for final file creation requests
145
* @param token - Upload token
146
* @returns Headers object with authorization and content-type
147
*/
148
function getHeadersForMkFile(token: string): { [key: string]: string };
149
150
/**
151
* Generate headers for chunk upload requests
152
* @param token - Upload token
153
* @returns Headers object with authorization and content-type
154
*/
155
function getHeadersForChunkUpload(token: string): { [key: string]: string };
156
```
157
158
**Usage Examples:**
159
160
```typescript
161
import { getHeadersForMkFile, getHeadersForChunkUpload } from "qiniu-js";
162
163
// Get headers for file completion
164
const token = "your-upload-token";
165
const mkFileHeaders = getHeadersForMkFile(token);
166
console.log(mkFileHeaders);
167
// Output: {
168
// "Authorization": "UpToken your-upload-token",
169
// "content-type": "application/json"
170
// }
171
172
// Get headers for chunk uploads
173
const chunkHeaders = getHeadersForChunkUpload(token);
174
console.log(chunkHeaders);
175
// Output: {
176
// "Authorization": "UpToken your-upload-token",
177
// "content-type": "application/octet-stream"
178
// }
179
180
// Use in custom HTTP requests
181
async function customChunkUpload(chunkData: Blob, uploadUrl: string, token: string) {
182
const headers = getHeadersForChunkUpload(token);
183
184
const response = await fetch(uploadUrl, {
185
method: 'PUT',
186
headers: {
187
...headers,
188
'Content-MD5': await calculateMD5(chunkData)
189
},
190
body: chunkData
191
});
192
193
return response.json();
194
}
195
```
196
197
## Advanced Utilities
198
199
### Image Compression Advanced Usage
200
201
Handle different scenarios and edge cases with image compression.
202
203
```typescript
204
import { compressImage, QiniuError, QiniuErrorName } from "qiniu-js";
205
206
async function smartCompress(file: File): Promise<File | Blob> {
207
// Skip compression for very small images
208
if (file.size < 100 * 1024) { // Less than 100KB
209
console.log('File too small, skipping compression');
210
return file;
211
}
212
213
// Skip compression for non-image files
214
if (!file.type.startsWith('image/')) {
215
console.log('Not an image file, skipping compression');
216
return file;
217
}
218
219
try {
220
// Try aggressive compression first
221
let result = await compressImage(file, {
222
quality: 0.7,
223
maxWidth: 1920,
224
maxHeight: 1080,
225
noCompressIfLarger: true
226
});
227
228
// If still too large, try more aggressive compression
229
if (result.dist.size > 2 * 1024 * 1024) { // > 2MB
230
result = await compressImage(file, {
231
quality: 0.5,
232
maxWidth: 1280,
233
maxHeight: 720,
234
noCompressIfLarger: true
235
});
236
}
237
238
console.log(`Compression ratio: ${(result.dist.size / file.size * 100).toFixed(1)}%`);
239
return result.dist;
240
241
} catch (error) {
242
if (error instanceof QiniuError) {
243
if (error.name === QiniuErrorName.UnsupportedFileType) {
244
console.log('Unsupported image format, using original');
245
return file;
246
} else if (error.name === QiniuErrorName.GetCanvasContextFailed) {
247
console.log('Canvas not available, using original');
248
return file;
249
}
250
}
251
252
console.error('Compression failed, using original:', error);
253
return file;
254
}
255
}
256
257
// Batch compression with progress
258
async function compressBatch(files: File[], onProgress?: (index: number, total: number) => void): Promise<(File | Blob)[]> {
259
const results: (File | Blob)[] = [];
260
261
for (let i = 0; i < files.length; i++) {
262
const compressed = await smartCompress(files[i]);
263
results.push(compressed);
264
265
if (onProgress) {
266
onProgress(i + 1, files.length);
267
}
268
}
269
270
return results;
271
}
272
```
273
274
### Base64 Utilities for Complex Scenarios
275
276
Handle various encoding scenarios for API parameters.
277
278
```typescript
279
import { urlSafeBase64Encode, urlSafeBase64Decode } from "qiniu-js";
280
281
// Create complex watermark configurations
282
function createWatermarkConfig(options: {
283
text?: string;
284
imageUrl?: string;
285
fontFamily?: string;
286
color?: string;
287
}) {
288
const params: string[] = [];
289
290
if (options.text) {
291
params.push(`text/${urlSafeBase64Encode(options.text)}`);
292
}
293
294
if (options.imageUrl) {
295
params.push(`image/${urlSafeBase64Encode(options.imageUrl)}`);
296
}
297
298
if (options.fontFamily) {
299
params.push(`font/${urlSafeBase64Encode(options.fontFamily)}`);
300
}
301
302
if (options.color) {
303
params.push(`fill/${urlSafeBase64Encode(options.color)}`);
304
}
305
306
return params.join('/');
307
}
308
309
// Usage
310
const watermarkParams = createWatermarkConfig({
311
text: "版权所有 © 2024",
312
fontFamily: "微软雅黑",
313
color: "#FFFFFF"
314
});
315
316
// Parse processing URLs
317
function parseProcessingUrl(url: string): any {
318
const params = url.split('/');
319
const result: any = {};
320
321
for (let i = 0; i < params.length; i += 2) {
322
if (i + 1 < params.length) {
323
const key = params[i];
324
const value = params[i + 1];
325
326
// Decode base64 encoded values
327
if (['text', 'font', 'image', 'fill'].includes(key)) {
328
result[key] = urlSafeBase64Decode(value);
329
} else {
330
result[key] = value;
331
}
332
}
333
}
334
335
return result;
336
}
337
```
338
339
### Upload Configuration Helpers
340
341
Utility functions for managing upload configurations and validating settings.
342
343
```typescript
344
// Validation helper for custom variables
345
function validateCustomVars(customVars: { [key: string]: string }): boolean {
346
return Object.keys(customVars).every(key => key.startsWith('x:'));
347
}
348
349
// Validation helper for metadata
350
function validateMetadata(metadata: { [key: string]: string }): boolean {
351
return Object.keys(metadata).every(key => key.startsWith('x-qn-meta-'));
352
}
353
354
// Configuration builder
355
class UploadConfigBuilder {
356
private config: any = {};
357
358
region(region: string) {
359
this.config.region = region;
360
return this;
361
}
362
363
retryCount(count: number) {
364
this.config.retryCount = Math.max(0, Math.min(10, count));
365
return this;
366
}
367
368
chunkSize(sizeMB: number) {
369
this.config.chunkSize = Math.max(1, Math.min(1000, sizeMB));
370
return this;
371
}
372
373
useCdn(enabled: boolean = true) {
374
this.config.useCdnDomain = enabled;
375
return this;
376
}
377
378
forceDirect(enabled: boolean = true) {
379
this.config.forceDirect = enabled;
380
return this;
381
}
382
383
build() {
384
return { ...this.config };
385
}
386
}
387
388
// Usage
389
const config = new UploadConfigBuilder()
390
.region('z0')
391
.retryCount(5)
392
.chunkSize(8)
393
.useCdn(true)
394
.build();
395
396
console.log(config);
397
// Output: {
398
// region: 'z0',
399
// retryCount: 5,
400
// chunkSize: 8,
401
// useCdnDomain: true
402
// }
403
```