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
Comprehensive error handling system with specific error types for different failure scenarios and automatic retry mechanisms.
The SDK provides a hierarchical error system for precise error handling and debugging.
enum QiniuErrorName {
// Input validation errors
InvalidFile = 'InvalidFile',
InvalidToken = 'InvalidToken',
InvalidMetadata = 'InvalidMetadata',
InvalidChunkSize = 'InvalidChunkSize',
InvalidCustomVars = 'InvalidCustomVars',
NotAvailableUploadHost = 'NotAvailableUploadHost',
// Cache and resume functionality errors
ReadCacheFailed = 'ReadCacheFailed',
InvalidCacheData = 'InvalidCacheData',
WriteCacheFailed = 'WriteCacheFailed',
RemoveCacheFailed = 'RemoveCacheFailed',
// Image compression errors
GetCanvasContextFailed = 'GetCanvasContextFailed',
UnsupportedFileType = 'UnsupportedFileType',
// Browser environment errors
FileReaderReadFailed = 'FileReaderReadFailed',
NotAvailableXMLHttpRequest = 'NotAvailableXMLHttpRequest',
InvalidProgressEventTarget = 'InvalidProgressEventTarget',
// Network and server errors
RequestError = 'RequestError'
}Foundation error class for all SDK errors.
/**
* Base error class for all Qiniu SDK errors
*/
class QiniuError implements Error {
public stack: string | undefined;
constructor(public name: QiniuErrorName, public message: string);
}Usage Example:
import { QiniuError, QiniuErrorName } from "qiniu-js";
try {
// Some SDK operation
} catch (error) {
if (error instanceof QiniuError) {
switch (error.name) {
case QiniuErrorName.InvalidFile:
console.error('File validation failed:', error.message);
break;
case QiniuErrorName.InvalidToken:
console.error('Token is invalid or expired:', error.message);
break;
default:
console.error('SDK error:', error.message);
}
}
}Specific error type for HTTP request failures with server response details.
/**
* Error class for HTTP request failures
*/
class QiniuRequestError extends QiniuError {
/**
* Legacy property for error type identification
* @deprecated Use instanceof instead
*/
public isRequestError = true;
/** Server response data if available */
public data?: any;
constructor(
public code: number,
public reqId: string,
message: string,
data?: any
);
}Usage Example:
import { upload, QiniuRequestError } from "qiniu-js";
const subscription = upload(file, key, token).subscribe({
error: (error) => {
if (error instanceof QiniuRequestError) {
console.error(`Request failed with HTTP ${error.code}`);
console.error(`Request ID: ${error.reqId}`);
if (error.data) {
console.error('Server response:', error.data);
}
// Handle specific HTTP codes
switch (error.code) {
case 401:
console.error('Unauthorized - check your token');
break;
case 403:
console.error('Forbidden - insufficient permissions');
break;
case 413:
console.error('File too large');
break;
case 599:
console.error('Server timeout - will retry automatically');
break;
default:
console.error('Request error:', error.message);
}
}
}
});Specialized error for network connectivity issues.
/**
* Error class for network connectivity issues
* Covers CORS, certificate, DNS, and connection failures
*/
class QiniuNetworkError extends QiniuRequestError {
constructor(message: string, reqId?: string);
}Usage Example:
import { upload, QiniuNetworkError, QiniuRequestError } from "qiniu-js";
const subscription = upload(file, key, token).subscribe({
error: (error) => {
if (error instanceof QiniuNetworkError) {
console.error('Network connectivity issue:', error.message);
// Suggest user checks connection, firewall, etc.
showNetworkErrorMessage();
} else if (error instanceof QiniuRequestError) {
console.error('Server request failed:', error.message);
// Handle server-side errors
} else {
console.error('Other error:', error.message);
// Handle validation and other errors
}
}
});Complete error handling strategy covering all error types.
import {
upload,
QiniuError,
QiniuRequestError,
QiniuNetworkError,
QiniuErrorName
} from "qiniu-js";
function handleUpload(file: File, key: string, token: string) {
const subscription = upload(file, key, token, {
fname: file.name
}, {
retryCount: 3,
region: 'z0'
}).subscribe({
next: (progress) => {
updateProgressUI(progress.total.percent);
},
error: (error) => {
if (error instanceof QiniuNetworkError) {
// Network issues - suggest user actions
showErrorMessage('Network connection failed. Please check your internet connection and try again.');
logError('network', error);
} else if (error instanceof QiniuRequestError) {
// Server errors - handle based on HTTP code
const isRetryable = [502, 503, 504, 599].includes(error.code);
if (isRetryable) {
showErrorMessage('Server temporarily unavailable. The upload will retry automatically.');
} else if (error.code === 401) {
showErrorMessage('Upload token expired. Please refresh and try again.');
refreshToken();
} else if (error.code === 413) {
showErrorMessage('File is too large for upload.');
} else {
showErrorMessage(`Upload failed: ${error.message}`);
}
logError('request', error, { code: error.code, reqId: error.reqId });
} else if (error instanceof QiniuError) {
// SDK validation and runtime errors
switch (error.name) {
case QiniuErrorName.InvalidFile:
showErrorMessage('Invalid file selected. Please choose a different file.');
break;
case QiniuErrorName.InvalidToken:
showErrorMessage('Upload token is invalid. Please refresh and try again.');
refreshToken();
break;
case QiniuErrorName.UnsupportedFileType:
showErrorMessage('File type not supported for compression.');
break;
case QiniuErrorName.GetCanvasContextFailed:
showErrorMessage('Image processing failed. Browser may not support required features.');
break;
default:
showErrorMessage(`Upload failed: ${error.message}`);
}
logError('sdk', error);
} else {
// Unexpected errors
showErrorMessage('An unexpected error occurred. Please try again.');
logError('unknown', error);
}
},
complete: (response) => {
showSuccessMessage('File uploaded successfully!');
console.log('Upload result:', response);
}
});
return subscription;
}
// Helper functions
function showErrorMessage(message: string) {
// Update UI with error message
document.getElementById('error-message').textContent = message;
}
function showSuccessMessage(message: string) {
// Update UI with success message
document.getElementById('success-message').textContent = message;
}
function updateProgressUI(percent: number) {
// Update progress bar
document.getElementById('progress-bar').style.width = `${percent}%`;
}
function logError(type: string, error: any, extra?: any) {
// Send error to logging service
console.error(`[${type}] Upload error:`, error, extra);
}
function refreshToken() {
// Request new token from server
// Implementation depends on your authentication system
}The SDK automatically handles retries for certain error conditions:
// Errors that trigger automatic retry
const RETRY_CODE_LIST = [0, 502, 503, 504, 599, 406];
// Errors that cause host switching
const FREEZE_CODE_LIST = [0, 502, 503, 504, 599];Custom Retry Configuration:
import { upload } from "qiniu-js";
// Configure retry behavior
const uploadWithRetry = upload(file, key, token, {
fname: file.name
}, {
retryCount: 5, // Retry up to 5 times
region: 'z0'
}).subscribe({
error: (error) => {
if (error instanceof QiniuRequestError) {
console.log(`Failed after ${5} retry attempts`);
}
}
});Prevent common validation errors by checking inputs before upload:
import { QiniuError, QiniuErrorName } from "qiniu-js";
function validateUploadInputs(file: File, token: string, putExtra?: any): string | null {
// File validation
if (!file || file.size === 0) {
return 'Please select a valid file';
}
if (file.size > 10000 * 1024 * 1024 * 1024) { // 10TB limit
return 'File size exceeds maximum limit of 10TB';
}
// Token validation
if (!token || typeof token !== 'string') {
return 'Upload token is required';
}
// Custom variables validation
if (putExtra?.customVars) {
for (const key of Object.keys(putExtra.customVars)) {
if (!key.startsWith('x:')) {
return `Custom variable key '${key}' must start with 'x:'`;
}
}
}
// Metadata validation
if (putExtra?.metadata) {
for (const key of Object.keys(putExtra.metadata)) {
if (!key.startsWith('x-qn-meta-')) {
return `Metadata key '${key}' must start with 'x-qn-meta-'`;
}
}
}
return null; // No validation errors
}
// Use validation before upload
function safeUpload(file: File, key: string, token: string, putExtra?: any) {
const validationError = validateUploadInputs(file, token, putExtra);
if (validationError) {
console.error('Validation failed:', validationError);
return;
}
return upload(file, key, token, putExtra);
}Install with Tessl CLI
npx tessl i tessl/npm-qiniu-js