CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-qiniu-js

JavaScript SDK for Qiniu Cloud Storage that enables browser-based file uploads with resumable transfer, image processing, and comprehensive error handling.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utilities

Helper functions for image compression, base64 encoding, and upload configuration management.

Capabilities

Image Compression

Client-side image compression to reduce file sizes before upload.

/**
 * Compress an image file on the client side
 * @param file - Image file to compress
 * @param options - Compression configuration
 * @returns Promise with compressed image and metadata
 */
function compressImage(file: File, options: CompressOptions): Promise<CompressResult>;

interface CompressOptions {
  /** Output quality (0-1), defaults to 0.92 */
  quality?: number;
  /** Don't compress if result is larger than original */
  noCompressIfLarger?: boolean;
  /** Maximum width constraint in pixels */
  maxWidth?: number;
  /** Maximum height constraint in pixels */
  maxHeight?: number;
}

interface CompressResult {
  /** Compressed image as Blob or original File */
  dist: Blob | File;
  /** Final width in pixels */
  width: number;
  /** Final height in pixels */
  height: number;
}

Usage Examples:

import { compressImage } from "qiniu-js";

// Basic compression
const fileInput = document.getElementById('imageInput') as HTMLInputElement;
const originalFile = fileInput.files[0];

try {
  const compressed = await compressImage(originalFile, {
    quality: 0.8,
    maxWidth: 1920,
    maxHeight: 1080,
    noCompressIfLarger: true
  });
  
  console.log(`Original: ${originalFile.size} bytes`);
  console.log(`Compressed: ${compressed.dist.size} bytes`);
  console.log(`Dimensions: ${compressed.width}x${compressed.height}`);
  
  // Use compressed image for upload
  const subscription = upload(compressed.dist, 'compressed-image.jpg', token);
  
} catch (error) {
  console.error('Compression failed:', error);
}

// Aggressive compression for thumbnails
async function createThumbnail(file: File): Promise<Blob> {
  const result = await compressImage(file, {
    quality: 0.7,
    maxWidth: 300,
    maxHeight: 300
  });
  
  return result.dist as Blob;
}

// Quality comparison
async function compressByQuality(file: File) {
  const qualities = [0.5, 0.7, 0.9];
  
  for (const quality of qualities) {
    const result = await compressImage(file, { quality });
    console.log(`Quality ${quality}: ${result.dist.size} bytes`);
  }
}

Base64 Encoding

URL-safe base64 encoding and decoding functions for Qiniu API compatibility.

/**
 * Encode data to URL-safe base64 format used by Qiniu APIs
 * @param v - Data to encode (string or other)
 * @returns URL-safe base64 encoded string
 */
function urlSafeBase64Encode(v: any): string;

/**
 * Decode URL-safe base64 data
 * @param v - URL-safe base64 encoded string
 * @returns Decoded string
 */
function urlSafeBase64Decode(v: any): string;

Usage Examples:

import { urlSafeBase64Encode, urlSafeBase64Decode } from "qiniu-js";

// Encode text for watermarks
const watermarkText = "© 2024 My Company";
const encodedText = urlSafeBase64Encode(watermarkText);
console.log('Encoded:', encodedText); // Safe for URLs

// Encode image URLs for watermarks
const logoUrl = "https://example.com/logo.png";
const encodedLogoUrl = urlSafeBase64Encode(logoUrl);

// Use in watermark operations
const watermarkUrl = `watermark/1/image/${encodedLogoUrl}/dissolve/80`;

// Decode data
const decodedText = urlSafeBase64Decode(encodedText);
console.log('Decoded:', decodedText); // "© 2024 My Company"

// Handle Chinese and Unicode text
const chineseText = "七牛云存储";
const encodedChinese = urlSafeBase64Encode(chineseText);
const decodedChinese = urlSafeBase64Decode(encodedChinese);
console.log('Chinese text preserved:', decodedChinese === chineseText);

HTTP Headers Generation

Generate properly formatted headers for different types of upload requests.

/**
 * Generate headers for final file creation requests
 * @param token - Upload token
 * @returns Headers object with authorization and content-type
 */
function getHeadersForMkFile(token: string): { [key: string]: string };

/**
 * Generate headers for chunk upload requests
 * @param token - Upload token
 * @returns Headers object with authorization and content-type
 */
function getHeadersForChunkUpload(token: string): { [key: string]: string };

Usage Examples:

import { getHeadersForMkFile, getHeadersForChunkUpload } from "qiniu-js";

// Get headers for file completion
const token = "your-upload-token";
const mkFileHeaders = getHeadersForMkFile(token);
console.log(mkFileHeaders);
// Output: {
//   "Authorization": "UpToken your-upload-token",
//   "content-type": "application/json"
// }

// Get headers for chunk uploads
const chunkHeaders = getHeadersForChunkUpload(token);
console.log(chunkHeaders);  
// Output: {
//   "Authorization": "UpToken your-upload-token",
//   "content-type": "application/octet-stream"
// }

// Use in custom HTTP requests
async function customChunkUpload(chunkData: Blob, uploadUrl: string, token: string) {
  const headers = getHeadersForChunkUpload(token);
  
  const response = await fetch(uploadUrl, {
    method: 'PUT',
    headers: {
      ...headers,
      'Content-MD5': await calculateMD5(chunkData)
    },
    body: chunkData
  });
  
  return response.json();
}

Advanced Utilities

Image Compression Advanced Usage

Handle different scenarios and edge cases with image compression.

import { compressImage, QiniuError, QiniuErrorName } from "qiniu-js";

async function smartCompress(file: File): Promise<File | Blob> {
  // Skip compression for very small images
  if (file.size < 100 * 1024) { // Less than 100KB
    console.log('File too small, skipping compression');
    return file;
  }
  
  // Skip compression for non-image files
  if (!file.type.startsWith('image/')) {
    console.log('Not an image file, skipping compression');
    return file;
  }
  
  try {
    // Try aggressive compression first
    let result = await compressImage(file, {
      quality: 0.7,
      maxWidth: 1920,
      maxHeight: 1080,
      noCompressIfLarger: true
    });
    
    // If still too large, try more aggressive compression
    if (result.dist.size > 2 * 1024 * 1024) { // > 2MB
      result = await compressImage(file, {
        quality: 0.5,
        maxWidth: 1280,
        maxHeight: 720,
        noCompressIfLarger: true
      });
    }
    
    console.log(`Compression ratio: ${(result.dist.size / file.size * 100).toFixed(1)}%`);
    return result.dist;
    
  } catch (error) {
    if (error instanceof QiniuError) {
      if (error.name === QiniuErrorName.UnsupportedFileType) {
        console.log('Unsupported image format, using original');
        return file;
      } else if (error.name === QiniuErrorName.GetCanvasContextFailed) {
        console.log('Canvas not available, using original');
        return file;
      }
    }
    
    console.error('Compression failed, using original:', error);
    return file;
  }
}

// Batch compression with progress
async function compressBatch(files: File[], onProgress?: (index: number, total: number) => void): Promise<(File | Blob)[]> {
  const results: (File | Blob)[] = [];
  
  for (let i = 0; i < files.length; i++) {
    const compressed = await smartCompress(files[i]);
    results.push(compressed);
    
    if (onProgress) {
      onProgress(i + 1, files.length);
    }
  }
  
  return results;
}

Base64 Utilities for Complex Scenarios

Handle various encoding scenarios for API parameters.

import { urlSafeBase64Encode, urlSafeBase64Decode } from "qiniu-js";

// Create complex watermark configurations
function createWatermarkConfig(options: {
  text?: string;
  imageUrl?: string;
  fontFamily?: string;
  color?: string;
}) {
  const params: string[] = [];
  
  if (options.text) {
    params.push(`text/${urlSafeBase64Encode(options.text)}`);
  }
  
  if (options.imageUrl) {
    params.push(`image/${urlSafeBase64Encode(options.imageUrl)}`);
  }
  
  if (options.fontFamily) {
    params.push(`font/${urlSafeBase64Encode(options.fontFamily)}`);
  }
  
  if (options.color) {
    params.push(`fill/${urlSafeBase64Encode(options.color)}`);
  }
  
  return params.join('/');
}

// Usage
const watermarkParams = createWatermarkConfig({
  text: "版权所有 © 2024",
  fontFamily: "微软雅黑",
  color: "#FFFFFF"
});

// Parse processing URLs
function parseProcessingUrl(url: string): any {
  const params = url.split('/');
  const result: any = {};
  
  for (let i = 0; i < params.length; i += 2) {
    if (i + 1 < params.length) {
      const key = params[i];
      const value = params[i + 1];
      
      // Decode base64 encoded values
      if (['text', 'font', 'image', 'fill'].includes(key)) {
        result[key] = urlSafeBase64Decode(value);
      } else {
        result[key] = value;
      }
    }
  }
  
  return result;
}

Upload Configuration Helpers

Utility functions for managing upload configurations and validating settings.

// Validation helper for custom variables
function validateCustomVars(customVars: { [key: string]: string }): boolean {
  return Object.keys(customVars).every(key => key.startsWith('x:'));
}

// Validation helper for metadata
function validateMetadata(metadata: { [key: string]: string }): boolean {
  return Object.keys(metadata).every(key => key.startsWith('x-qn-meta-'));
}

// Configuration builder
class UploadConfigBuilder {
  private config: any = {};
  
  region(region: string) {
    this.config.region = region;
    return this;
  }
  
  retryCount(count: number) {
    this.config.retryCount = Math.max(0, Math.min(10, count));
    return this;
  }
  
  chunkSize(sizeMB: number) {
    this.config.chunkSize = Math.max(1, Math.min(1000, sizeMB));
    return this;
  }
  
  useCdn(enabled: boolean = true) {
    this.config.useCdnDomain = enabled;
    return this;
  }
  
  forceDirect(enabled: boolean = true) {
    this.config.forceDirect = enabled;
    return this;
  }
  
  build() {
    return { ...this.config };
  }
}

// Usage
const config = new UploadConfigBuilder()
  .region('z0')
  .retryCount(5)
  .chunkSize(8)
  .useCdn(true)
  .build();

console.log(config);
// Output: {
//   region: 'z0',
//   retryCount: 5,
//   chunkSize: 8,
//   useCdnDomain: true
// }

Install with Tessl CLI

npx tessl i tessl/npm-qiniu-js

docs

configuration.md

error-handling.md

image-processing.md

index.md

upload.md

utilities.md

tile.json