javascript

examples

examples.js
/**
 * Blob and File API Examples
 *
 * Demonstrates working with binary data and files in JavaScript
 */

// =============================================================================
// 1. Creating Blobs
// =============================================================================

/**
 * Create various types of Blobs
 */
function createBlobs() {
  // Text Blob
  const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' });
  console.log('Text Blob:', { size: textBlob.size, type: textBlob.type });

  // JSON Blob
  const data = { name: 'John', age: 30, hobbies: ['coding', 'reading'] };
  const jsonBlob = new Blob([JSON.stringify(data, null, 2)], {
    type: 'application/json',
  });
  console.log('JSON Blob:', { size: jsonBlob.size, type: jsonBlob.type });

  // HTML Blob
  const htmlContent = `
        <!DOCTYPE html>
        <html>
            <head><title>Generated Page</title></head>
            <body><h1>Hello from Blob!</h1></body>
        </html>
    `;
  const htmlBlob = new Blob([htmlContent], { type: 'text/html' });

  // CSV Blob
  const csvContent = 'Name,Age,City\nJohn,30,NYC\nJane,25,LA\nBob,35,Chicago';
  const csvBlob = new Blob([csvContent], { type: 'text/csv' });

  // Binary Blob from Uint8Array
  const bytes = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in ASCII
  const binaryBlob = new Blob([bytes], { type: 'application/octet-stream' });

  // Multi-part Blob
  const parts = [
    'Part 1: Header\n',
    '---\n',
    'Part 2: Body\n',
    '---\n',
    'Part 3: Footer',
  ];
  const multiPartBlob = new Blob(parts, { type: 'text/plain' });

  return {
    textBlob,
    jsonBlob,
    htmlBlob,
    csvBlob,
    binaryBlob,
    multiPartBlob,
  };
}

// =============================================================================
// 2. Creating Files
// =============================================================================

/**
 * Create File objects programmatically
 */
function createFiles() {
  // Simple text file
  const textFile = new File(['This is the file content'], 'document.txt', {
    type: 'text/plain',
    lastModified: Date.now(),
  });

  console.log('File properties:', {
    name: textFile.name,
    size: textFile.size,
    type: textFile.type,
    lastModified: new Date(textFile.lastModified).toISOString(),
  });

  // JSON file
  const configData = {
    version: '1.0.0',
    settings: { theme: 'dark', language: 'en' },
  };
  const jsonFile = new File(
    [JSON.stringify(configData, null, 2)],
    'config.json',
    { type: 'application/json' }
  );

  // SVG file
  const svgContent = `
        <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
            <circle cx="50" cy="50" r="40" fill="blue"/>
        </svg>
    `;
  const svgFile = new File([svgContent], 'circle.svg', {
    type: 'image/svg+xml',
  });

  return { textFile, jsonFile, svgFile };
}

// =============================================================================
// 3. Reading Files with FileReader
// =============================================================================

/**
 * FileReader wrapper with Promise
 */
class FileReaderAsync {
  static readAsText(blob, encoding = 'UTF-8') {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.readAsText(blob, encoding);
    });
  }

  static readAsDataURL(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.readAsDataURL(blob);
    });
  }

  static readAsArrayBuffer(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.readAsArrayBuffer(blob);
    });
  }

  static readAsBinaryString(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.readAsBinaryString(blob);
    });
  }

  static readWithProgress(blob, method, onProgress) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      reader.onprogress = (event) => {
        if (event.lengthComputable && onProgress) {
          onProgress({
            loaded: event.loaded,
            total: event.total,
            percent: (event.loaded / event.total) * 100,
          });
        }
      };

      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);

      reader[method](blob);
    });
  }
}

// Usage example
async function readFileExamples() {
  const textBlob = new Blob(['Hello, FileReader!'], { type: 'text/plain' });

  // Read as text
  const text = await FileReaderAsync.readAsText(textBlob);
  console.log('Text:', text);

  // Read as data URL
  const dataUrl = await FileReaderAsync.readAsDataURL(textBlob);
  console.log('Data URL:', dataUrl);

  // Read as ArrayBuffer
  const buffer = await FileReaderAsync.readAsArrayBuffer(textBlob);
  console.log('ArrayBuffer:', buffer.byteLength, 'bytes');

  // Modern alternative (blob.text(), blob.arrayBuffer())
  const modernText = await textBlob.text();
  const modernBuffer = await textBlob.arrayBuffer();
}

// =============================================================================
// 4. Blob URLs
// =============================================================================

/**
 * Create and manage Blob URLs
 */
class BlobURLManager {
  constructor() {
    this.urls = new Set();
  }

  create(blob) {
    const url = URL.createObjectURL(blob);
    this.urls.add(url);
    return url;
  }

  revoke(url) {
    URL.revokeObjectURL(url);
    this.urls.delete(url);
  }

  revokeAll() {
    this.urls.forEach((url) => URL.revokeObjectURL(url));
    this.urls.clear();
  }

  get count() {
    return this.urls.size;
  }
}

// Example: Create downloadable link
function createDownloadLink(blob, filename) {
  const url = URL.createObjectURL(blob);

  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.textContent = `Download ${filename}`;

  // Cleanup URL after download
  link.onclick = () => {
    setTimeout(() => URL.revokeObjectURL(url), 100);
  };

  return link;
}

// Example: Image preview
function createImagePreview(file) {
  return new Promise((resolve, reject) => {
    if (!file.type.startsWith('image/')) {
      reject(new Error('Not an image file'));
      return;
    }

    const url = URL.createObjectURL(file);
    const img = new Image();

    img.onload = () => {
      URL.revokeObjectURL(url);
      resolve(img);
    };

    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error('Failed to load image'));
    };

    img.src = url;
  });
}

// =============================================================================
// 5. Data Conversions
// =============================================================================

const DataConverter = {
  // Blob conversions
  async blobToText(blob) {
    return blob.text();
  },

  textToBlob(text, type = 'text/plain') {
    return new Blob([text], { type });
  },

  async blobToBase64(blob) {
    return FileReaderAsync.readAsDataURL(blob);
  },

  base64ToBlob(base64, type = 'application/octet-stream') {
    // Remove data URL prefix if present
    const base64Data = base64.includes(',') ? base64.split(',')[1] : base64;
    const binaryString = atob(base64Data);
    const bytes = new Uint8Array(binaryString.length);

    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i);
    }

    return new Blob([bytes], { type });
  },

  async blobToArrayBuffer(blob) {
    return blob.arrayBuffer();
  },

  arrayBufferToBlob(buffer, type = 'application/octet-stream') {
    return new Blob([buffer], { type });
  },

  // String conversions
  stringToArrayBuffer(str) {
    return new TextEncoder().encode(str).buffer;
  },

  arrayBufferToString(buffer) {
    return new TextDecoder().decode(buffer);
  },

  // Hex conversions
  arrayBufferToHex(buffer) {
    return Array.from(new Uint8Array(buffer))
      .map((b) => b.toString(16).padStart(2, '0'))
      .join('');
  },

  hexToArrayBuffer(hex) {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < hex.length; i += 2) {
      bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
    }
    return bytes.buffer;
  },
};

// =============================================================================
// 6. Blob Slicing and Streaming
// =============================================================================

/**
 * Read large files in chunks
 */
async function readInChunks(blob, chunkSize = 1024 * 1024, onChunk) {
  let offset = 0;
  let chunkIndex = 0;

  while (offset < blob.size) {
    const chunk = blob.slice(offset, offset + chunkSize);
    const data = await chunk.arrayBuffer();

    await onChunk({
      index: chunkIndex,
      data,
      offset,
      size: chunk.size,
      isLast: offset + chunkSize >= blob.size,
    });

    offset += chunkSize;
    chunkIndex++;
  }

  return chunkIndex;
}

/**
 * Concatenate multiple Blobs
 */
function concatenateBlobs(blobs, type) {
  return new Blob(blobs, { type: type || blobs[0]?.type });
}

/**
 * Split Blob into equal parts
 */
function splitBlob(blob, numParts) {
  const partSize = Math.ceil(blob.size / numParts);
  const parts = [];

  for (let i = 0; i < numParts; i++) {
    const start = i * partSize;
    const end = Math.min(start + partSize, blob.size);
    parts.push(blob.slice(start, end));
  }

  return parts;
}

// =============================================================================
// 7. File Downloads
// =============================================================================

const FileDownloader = {
  /**
   * Download content as a file
   */
  download(content, filename, type = 'text/plain') {
    const blob = new Blob([content], { type });
    this.downloadBlob(blob, filename);
  },

  /**
   * Download Blob as file
   */
  downloadBlob(blob, filename) {
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.style.display = 'none';

    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);

    URL.revokeObjectURL(url);
  },

  /**
   * Download JSON data
   */
  downloadJSON(data, filename) {
    const json = JSON.stringify(data, null, 2);
    this.download(json, filename, 'application/json');
  },

  /**
   * Download CSV data
   */
  downloadCSV(rows, filename, headers) {
    let csv = '';

    if (headers) {
      csv += headers.join(',') + '\n';
    }

    rows.forEach((row) => {
      csv +=
        row
          .map((cell) => {
            // Escape quotes and wrap in quotes if needed
            if (
              typeof cell === 'string' &&
              (cell.includes(',') || cell.includes('"'))
            ) {
              return `"${cell.replace(/"/g, '""')}"`;
            }
            return cell;
          })
          .join(',') + '\n';
    });

    this.download(csv, filename, 'text/csv');
  },

  /**
   * Download from URL
   */
  async downloadFromURL(url, filename) {
    const response = await fetch(url);
    const blob = await response.blob();
    this.downloadBlob(blob, filename);
  },
};

// =============================================================================
// 8. File Uploads
// =============================================================================

const FileUploader = {
  /**
   * Upload file with FormData
   */
  async upload(file, url, additionalData = {}) {
    const formData = new FormData();
    formData.append('file', file);

    Object.entries(additionalData).forEach(([key, value]) => {
      formData.append(key, value);
    });

    const response = await fetch(url, {
      method: 'POST',
      body: formData,
    });

    if (!response.ok) {
      throw new Error(`Upload failed: ${response.statusText}`);
    }

    return response.json();
  },

  /**
   * Upload with progress tracking
   */
  uploadWithProgress(file, url, onProgress) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      const formData = new FormData();
      formData.append('file', file);

      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable) {
          onProgress({
            loaded: e.loaded,
            total: e.total,
            percent: (e.loaded / e.total) * 100,
          });
        }
      });

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          resolve(JSON.parse(xhr.responseText));
        } else {
          reject(new Error(`Upload failed: ${xhr.statusText}`));
        }
      });

      xhr.addEventListener('error', () => reject(new Error('Upload failed')));
      xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));

      xhr.open('POST', url);
      xhr.send(formData);
    });
  },

  /**
   * Upload multiple files
   */
  async uploadMultiple(files, url, onProgress) {
    const results = [];
    let completed = 0;

    for (const file of files) {
      const result = await this.upload(file, url);
      results.push(result);
      completed++;

      if (onProgress) {
        onProgress({
          completed,
          total: files.length,
          percent: (completed / files.length) * 100,
        });
      }
    }

    return results;
  },

  /**
   * Upload in chunks
   */
  async uploadChunked(file, url, chunkSize = 1024 * 1024, onProgress) {
    const totalChunks = Math.ceil(file.size / chunkSize);
    const uploadId = Date.now().toString(36);

    for (let i = 0; i < totalChunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, file.size);
      const chunk = file.slice(start, end);

      const formData = new FormData();
      formData.append('chunk', chunk);
      formData.append('chunkIndex', i.toString());
      formData.append('totalChunks', totalChunks.toString());
      formData.append('uploadId', uploadId);
      formData.append('fileName', file.name);

      await fetch(url, {
        method: 'POST',
        body: formData,
      });

      if (onProgress) {
        onProgress({
          chunk: i + 1,
          totalChunks,
          percent: ((i + 1) / totalChunks) * 100,
        });
      }
    }

    return uploadId;
  },
};

// =============================================================================
// 9. Image Processing
// =============================================================================

const ImageProcessor = {
  /**
   * Load image from File/Blob
   */
  async loadImage(fileOrBlob) {
    return new Promise((resolve, reject) => {
      const url = URL.createObjectURL(fileOrBlob);
      const img = new Image();

      img.onload = () => {
        URL.revokeObjectURL(url);
        resolve(img);
      };

      img.onerror = () => {
        URL.revokeObjectURL(url);
        reject(new Error('Failed to load image'));
      };

      img.src = url;
    });
  },

  /**
   * Resize image
   */
  async resize(file, maxWidth, maxHeight, quality = 0.9) {
    const img = await this.loadImage(file);

    let { width, height } = img;

    // Calculate new dimensions
    if (width > maxWidth) {
      height = (height * maxWidth) / width;
      width = maxWidth;
    }

    if (height > maxHeight) {
      width = (width * maxHeight) / height;
      height = maxHeight;
    }

    // Draw to canvas
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0, width, height);

    // Convert to Blob
    return new Promise((resolve) => {
      canvas.toBlob(resolve, file.type, quality);
    });
  },

  /**
   * Crop image
   */
  async crop(file, x, y, width, height) {
    const img = await this.loadImage(file);

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, x, y, width, height, 0, 0, width, height);

    return new Promise((resolve) => {
      canvas.toBlob(resolve, file.type);
    });
  },

  /**
   * Convert format
   */
  async convertFormat(file, targetType, quality = 0.9) {
    const img = await this.loadImage(file);

    const canvas = document.createElement('canvas');
    canvas.width = img.width;
    canvas.height = img.height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(img, 0, 0);

    return new Promise((resolve) => {
      canvas.toBlob(resolve, targetType, quality);
    });
  },

  /**
   * Get image dimensions
   */
  async getDimensions(file) {
    const img = await this.loadImage(file);
    return { width: img.width, height: img.height };
  },

  /**
   * Create thumbnail
   */
  async createThumbnail(file, size = 150) {
    return this.resize(file, size, size, 0.8);
  },
};

// =============================================================================
// 10. File Type Detection
// =============================================================================

const FileTypeDetector = {
  // File signatures (magic numbers)
  signatures: {
    'image/jpeg': [0xff, 0xd8, 0xff],
    'image/png': [0x89, 0x50, 0x4e, 0x47],
    'image/gif': [0x47, 0x49, 0x46],
    'image/webp': [0x52, 0x49, 0x46, 0x46],
    'application/pdf': [0x25, 0x50, 0x44, 0x46],
    'application/zip': [0x50, 0x4b, 0x03, 0x04],
    'video/mp4': [0x00, 0x00, 0x00], // ftyp check needed
    'audio/mpeg': [0x49, 0x44, 0x33], // ID3
  },

  /**
   * Detect type by magic bytes
   */
  async detectByBytes(file) {
    const header = file.slice(0, 12);
    const buffer = await header.arrayBuffer();
    const bytes = new Uint8Array(buffer);

    for (const [type, signature] of Object.entries(this.signatures)) {
      if (this.matchSignature(bytes, signature)) {
        return type;
      }
    }

    return file.type || 'application/octet-stream';
  },

  matchSignature(bytes, signature) {
    for (let i = 0; i < signature.length; i++) {
      if (bytes[i] !== signature[i]) {
        return false;
      }
    }
    return true;
  },

  /**
   * Get type by extension
   */
  getTypeByExtension(filename) {
    const ext = filename.split('.').pop().toLowerCase();
    const types = {
      // Images
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      png: 'image/png',
      gif: 'image/gif',
      webp: 'image/webp',
      svg: 'image/svg+xml',
      // Documents
      pdf: 'application/pdf',
      doc: 'application/msword',
      docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      // Data
      json: 'application/json',
      xml: 'application/xml',
      csv: 'text/csv',
      txt: 'text/plain',
      // Code
      html: 'text/html',
      css: 'text/css',
      js: 'application/javascript',
      // Archives
      zip: 'application/zip',
      rar: 'application/x-rar-compressed',
      // Audio/Video
      mp3: 'audio/mpeg',
      mp4: 'video/mp4',
      wav: 'audio/wav',
    };
    return types[ext] || 'application/octet-stream';
  },

  /**
   * Validate file type
   */
  async validate(file, allowedTypes) {
    const detectedType = await this.detectByBytes(file);
    return allowedTypes.some((type) => {
      if (type.endsWith('/*')) {
        return detectedType.startsWith(type.slice(0, -1));
      }
      return detectedType === type;
    });
  },
};

// =============================================================================
// Export
// =============================================================================

if (typeof module !== 'undefined' && module.exports) {
  module.exports = {
    createBlobs,
    createFiles,
    FileReaderAsync,
    BlobURLManager,
    DataConverter,
    readInChunks,
    concatenateBlobs,
    splitBlob,
    FileDownloader,
    FileUploader,
    ImageProcessor,
    FileTypeDetector,
  };
}
Examples - JavaScript Tutorial | DeepML