JavaScript SDK for Qiniu Cloud Storage that enables browser-based file uploads with resumable transfer, image processing, and comprehensive error handling.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Core file upload functionality with automatic upload strategy selection, progress tracking, and resume capabilities for large files.
Main upload function that automatically selects between direct upload (≤4MB) and resumable chunked upload (>4MB).
/**
* Upload a file to Qiniu Cloud Storage
* @param file - File object to upload
* @param key - Target filename, null for auto-generated key
* @param token - Upload token from server
* @param putExtra - Optional upload metadata and custom variables
* @param config - Optional upload configuration
* @returns Observable for tracking upload progress and completion
*/
function upload(
file: File,
key: string | null | undefined,
token: string,
putExtra?: Partial<Extra>,
config?: Config
): Observable<UploadProgress, QiniuError | QiniuRequestError | QiniuNetworkError, UploadCompleteData>;Usage Examples:
import { upload, region } from "qiniu-js";
// Basic upload with minimal configuration
const file = document.getElementById('fileInput').files[0];
const token = await getTokenFromServer(); // Your server implementation
const subscription = upload(file, null, token).subscribe({
next: (progress) => {
console.log(`Upload progress: ${progress.total.percent.toFixed(1)}%`);
console.log(`Uploaded: ${progress.total.loaded} / ${progress.total.size} bytes`);
},
error: (error) => {
if (error instanceof QiniuRequestError) {
console.error(`Request failed with code ${error.code}:`, error.message);
} else if (error instanceof QiniuNetworkError) {
console.error('Network error:', error.message);
} else {
console.error('Upload error:', error.message);
}
},
complete: (response) => {
console.log('Upload completed successfully:', response);
// response contains server response data
}
});
// Upload with custom variables and metadata
const uploadWithMetadata = upload(file, 'user-photos/avatar.jpg', token, {
fname: file.name,
customVars: {
'x:userId': '12345',
'x:uploadSource': 'profile-page'
},
metadata: {
'x-qn-meta-author': 'John Doe',
'x-qn-meta-description': 'Profile avatar'
},
mimeType: 'image/jpeg'
}, {
region: region.z0,
useCdnDomain: true,
retryCount: 5,
chunkSize: 8, // 8MB chunks for large files
concurrentRequestLimit: 3
});Configuration options for controlling upload behavior and performance.
interface Config {
/** Enable CDN domain for uploads (recommended) */
useCdnDomain?: boolean;
/** Upload region selection */
region?: typeof region[keyof typeof region];
/** Force direct upload even for large files */
forceDirect?: boolean;
/** Number of retry attempts on failure */
retryCount?: number;
/** Chunk size in MB for resumable uploads */
chunkSize?: number;
/** Maximum concurrent upload requests */
concurrentRequestLimit?: number;
/** Upload protocol */
upprotocol?: 'https' | 'http';
/** Disable statistics reporting */
disableStatisticsReport?: boolean;
}Additional information and custom data for uploads.
interface Extra {
/** Original filename */
fname: string;
/** Custom variables (must start with 'x:') */
customVars?: { [key: string]: string };
/** Metadata (must start with 'x-qn-meta-') */
metadata?: { [key: string]: string };
/** MIME type override */
mimeType?: string;
}Custom Variables Example:
// Custom variables for application use
const putExtra = {
fname: 'document.pdf',
customVars: {
'x:userId': '12345',
'x:category': 'documents',
'x:public': 'false'
},
metadata: {
'x-qn-meta-title': 'Important Document',
'x-qn-meta-author': 'Jane Smith'
}
};Detailed progress information during upload operations.
interface UploadProgress {
/** Overall upload progress */
total: ProgressCompose;
/** Upload session information for resumable uploads */
uploadInfo?: UploadInfo;
/** Individual chunk progress for large files */
chunks?: ProgressCompose[];
}
interface ProgressCompose {
/** Total size in bytes */
size: number;
/** Bytes uploaded so far */
loaded: number;
/** Completion percentage (0-100) */
percent: number;
/** Whether data was loaded from cache (resume) */
fromCache?: boolean;
}
interface UploadInfo {
/** Upload session ID for resumable uploads */
id: string;
/** Upload endpoint URL */
url: string;
}Upload function returns an Observable for reactive programming patterns.
interface Observable<T, E, R> {
subscribe(observer: {
next?: (value: T) => void;
error?: (error: E) => void;
complete?: (result: R) => void;
}): Subscription;
}
interface Subscription {
/** Cancel the upload operation */
unsubscribe(): void;
}Advanced Usage Example:
import { upload, QiniuRequestError, QiniuNetworkError } from "qiniu-js";
let uploadSubscription;
function startUpload(file, token) {
uploadSubscription = upload(file, null, token, {
fname: file.name
}, {
region: region.z2,
retryCount: 3,
chunkSize: 4
}).subscribe({
next: (progress) => {
updateProgressBar(progress.total.percent);
// Show chunk details for large files
if (progress.chunks) {
progress.chunks.forEach((chunk, index) => {
console.log(`Chunk ${index}: ${chunk.percent}% ${chunk.fromCache ? '(cached)' : ''}`);
});
}
},
error: (error) => {
if (error instanceof QiniuRequestError) {
// Server returned an error
handleServerError(error.code, error.data);
} else if (error instanceof QiniuNetworkError) {
// Network connectivity issue
handleNetworkError();
} else {
// Other errors (validation, file issues, etc.)
handleGenericError(error.message);
}
},
complete: (response) => {
// Upload successful
console.log('File uploaded successfully:', response);
}
});
}
function cancelUpload() {
if (uploadSubscription) {
uploadSubscription.unsubscribe();
console.log('Upload cancelled');
}
}Install with Tessl CLI
npx tessl i tessl/npm-qiniu-js