🎯 Recommended Samples
Balanced sample collections from various categories for you to explore
Web File Operations TypeScript Samples
Web TypeScript file operations examples including text file read/write, file copy/move, directory traversal, and file validation
💻 Text File Read/Write typescript
🟢 simple
⭐⭐
Read from and write to text files with various encoding options using File API and Blob
⏱️ 20 min
🏷️ typescript, web, file operations
Prerequisites:
Basic TypeScript knowledge, File API
// Web TypeScript Text File Read/Write Examples
// Using File API, Blob API, and FileReader for browser-based file operations
// 1. Reading Text Files
class TextFileReader {
// Read file as text
async readAsText(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsText(file);
});
}
// Read file with specific encoding
async readAsTextWithEncoding(file: File, encoding: string): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsText(file, encoding);
});
}
// Read file as data URL
async readAsDataURL(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsDataURL(file);
});
}
// Read file as ArrayBuffer
async readAsArrayBuffer(file: File): Promise<ArrayBuffer> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as ArrayBuffer);
};
reader.onerror = () => {
reject(new Error('Failed to read file'));
};
reader.readAsArrayBuffer(file);
});
}
// Read file line by line
async *readLines(file: File): AsyncGenerator<string> {
const text = await this.readAsText(file);
const lines = text.split('\n');
for (const line of lines) {
yield line;
}
}
// Read file with chunking (for large files)
async *readChunks(file: File, chunkSize: number = 1024 * 1024): AsyncGenerator<Blob> {
const fileSize = file.size;
let offset = 0;
while (offset < fileSize) {
const end = Math.min(offset + chunkSize, fileSize);
const chunk = file.slice(offset, end);
yield chunk;
offset = end;
}
}
}
// 2. Writing Text Files
class TextFileWriter {
// Write text to file (triggers download)
writeText(fileName: string, text: string, mimeType: string = 'text/plain'): void {
const blob = new Blob([text], { type: mimeType });
this.downloadBlob(blob, fileName);
}
// Write text lines to file
writeLines(fileName: string, lines: string[]): void {
const text = lines.join('\n');
this.writeText(fileName, text);
}
// Write text with specific encoding
writeTextWithEncoding(fileName: string, text: string, encoding: string): void {
const encoder = new TextEncoder();
const uint8Array = encoder.encode(text);
const blob = new Blob([uint8Array], { type: `text/plain;charset=${encoding}` });
this.downloadBlob(blob, fileName);
}
// Append text to file (returns new blob)
appendToBlob(existingBlob: Blob, text: string): Blob {
return new Blob([existingBlob, text], { type: existingBlob.type });
}
// Download blob as file
private downloadBlob(blob: Blob, fileName: string): void {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
}
// 3. File Info Helper
class FileInfoHelper {
// Get file size in readable format
getReadableSize(bytes: number): string {
const kb = bytes / 1024;
const mb = kb / 1024;
const gb = mb / 1024;
if (gb >= 1) {
return `${gb.toFixed(2)} GB`;
} else if (mb >= 1) {
return `${mb.toFixed(2)} MB`;
} else if (kb >= 1) {
return `${kb.toFixed(2)} KB`;
} else {
return `${bytes} bytes`;
}
}
// Get file extension
getExtension(file: File): string {
const name = file.name;
const lastDotIndex = name.lastIndexOf('.');
return lastDotIndex !== -1 ? name.substring(lastDotIndex + 1) : '';
}
// Get file name without extension
getFileNameWithoutExtension(file: File): string {
const name = file.name;
const lastDotIndex = name.lastIndexOf('.');
return lastDotIndex !== -1 ? name.substring(0, lastDotIndex) : name;
}
// Get MIME type
getMimeType(file: File): string {
return file.type || 'application/octet-stream';
}
// Get file info object
getFileDetails(file: File): Record<string, any> {
return {
name: file.name,
size: file.size,
readableSize: this.getReadableSize(file.size),
type: file.type,
lastModified: new Date(file.lastModified),
extension: this.getExtension(file),
nameWithoutExtension: this.getFileNameWithoutExtension(file)
};
}
// Print file info
printFileInfo(file: File): void {
const info = this.getFileDetails(file);
console.log('=== File Information ===');
console.log(`Name: ${info.name}`);
console.log(`Size: ${info.readableSize}`);
console.log(`Type: ${info.type}`);
console.log(`Extension: ${info.extension}`);
console.log(`Last Modified: ${info.lastModified}`);
}
}
// 4. File Selection Helper
class FileSelector {
// Select single file
async selectFile(acceptType: string = '*'): Promise<File> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = acceptType;
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(files[0]);
} else {
reject(new Error('No file selected'));
}
};
input.oncancel = () => {
reject(new Error('File selection cancelled'));
};
input.click();
});
}
// Select multiple files
async selectMultipleFiles(acceptType: string = '*'): Promise<File[]> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = acceptType;
input.multiple = true;
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(Array.from(files));
} else {
reject(new Error('No files selected'));
}
};
input.oncancel = () => {
reject(new Error('File selection cancelled'));
};
input.click();
});
}
// Select files by MIME type
async selectByMimeType(mimeType: string): Promise<File[]> {
return this.selectMultipleFiles(mimeType);
}
}
// 5. File Validation
class FileValidator {
// Validate file type
isValidType(file: File, allowedTypes: string[]): boolean {
return allowedTypes.some(type => file.type === type);
}
// Validate file extension
isValidExtension(file: File, allowedExtensions: string[]): boolean {
const extension = file.name.split('.').pop()?.toLowerCase();
return extension !== undefined && allowedExtensions.includes(extension);
}
// Validate file size
isValidSize(file: File, maxSizeInBytes: number): boolean {
return file.size <= maxSizeInBytes;
}
// Validate text file
isTextFile(file: File): boolean {
const textTypes = ['text/plain', 'text/html', 'text/css', 'text/javascript',
'application/json', 'application/xml'];
return this.isValidType(file, textTypes);
}
// Validate and throw error
validate(file: File, options: {
maxSize?: number;
allowedTypes?: string[];
allowedExtensions?: string[];
}): void {
if (options.maxSize && !this.isValidSize(file, options.maxSize)) {
throw new Error(`File size exceeds maximum size of ${options.maxSize} bytes`);
}
if (options.allowedTypes && !this.isValidType(file, options.allowedTypes)) {
throw new Error(`File type not allowed. Allowed types: ${options.allowedTypes.join(', ')}`);
}
if (options.allowedExtensions && !this.isValidExtension(file, options.allowedExtensions)) {
throw new Error(`File extension not allowed. Allowed extensions: ${options.allowedExtensions.join(', ')}`);
}
}
}
// 6. Batch File Operations
class BatchFileOperations {
private reader = new TextFileReader();
private fileInfo = new FileInfoHelper();
// Read multiple files
async readMultipleFiles(files: File[]): Promise<Map<File, string>> {
const results = new Map<File, string>();
for (const file of files) {
try {
const content = await this.reader.readAsText(file);
results.set(file, content);
} catch (error) {
console.error(`Failed to read ${file.name}:`, error);
}
}
return results;
}
// Get total size of files
getTotalSize(files: File[]): number {
return files.reduce((total, file) => total + file.size, 0);
}
// Filter files by type
filterByType(files: File[], mimeType: string): File[] {
return files.filter(file => file.type === mimeType);
}
// Filter files by extension
filterByExtension(files: File[], extension: string): File[] {
return files.filter(file => file.name.endsWith(extension));
}
// Sort files by name
sortByName(files: File[], ascending: boolean = true): File[] {
return [...files].sort((a, b) => {
return ascending
? a.name.localeCompare(b.name)
: b.name.localeCompare(a.name);
});
}
// Sort files by size
sortBySize(files: File[], ascending: boolean = true): File[] {
return [...files].sort((a, b) => {
return ascending ? a.size - b.size : b.size - a.size;
});
}
// Group files by type
groupByType(files: File[]): Map<string, File[]> {
const groups = new Map<string, File[]>();
for (const file of files) {
const type = file.type || 'unknown';
if (!groups.has(type)) {
groups.set(type, []);
}
groups.get(type)!.push(file);
}
return groups;
}
}
// 7. File Processing Utilities
class FileProcessingUtils {
// Count words in text
countWords(text: string): number {
const words = text.trim().split(/\s+/);
return words.filter(word => word.length > 0).length;
}
// Count lines in text
countLines(text: string): number {
return text.split('\n').length;
}
// Count characters in text
countCharacters(text: string, includeSpaces: boolean = true): number {
if (includeSpaces) {
return text.length;
}
return text.replace(/\s/g, '').length;
}
// Search for text in file
async searchInFile(file: File, searchText: string): Promise<number[]> {
const reader = new TextFileReader();
const content = await reader.readAsText(file);
const lines = content.split('\n');
const matches: number[] = [];
for (let i = 0; i < lines.length; i++) {
if (lines[i].toLowerCase().includes(searchText.toLowerCase())) {
matches.push(i + 1);
}
}
return matches;
}
// Replace text in file
async replaceInFile(file: File, searchText: string, replaceWith: string): Promise<string> {
const reader = new TextFileReader();
const content = await reader.readAsText(file);
return content.replaceAll(searchText, replaceWith);
}
// Convert file to JSON (if CSV or similar)
async parseCSV(file: File): Promise<string[][]> {
const reader = new TextFileReader();
const content = await reader.readAsText(file);
const lines = content.split('\n');
return lines.map(line => {
const values: string[] = [];
let current = '';
let inQuotes = false;
for (let i = 0; i < line.length; i++) {
const char = line[i];
if (char === '"') {
inQuotes = !inQuotes;
} else if (char === ',' && !inQuotes) {
values.push(current.trim());
current = '';
} else {
current += char;
}
}
values.push(current.trim());
return values;
});
}
}
// 8. File Download Helper
class FileDownloadHelper {
// Download text as file
downloadText(fileName: string, text: string): void {
const blob = new Blob([text], { type: 'text/plain' });
this.downloadBlob(blob, fileName);
}
// Download JSON as file
downloadJSON(fileName: string, data: any): void {
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
this.downloadBlob(blob, fileName);
}
// Download data URL as file
downloadDataURL(fileName: string, dataURL: string): void {
const link = document.createElement('a');
link.href = dataURL;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Download blob
downloadBlob(blob: Blob, fileName: string): void {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
}
// Usage Examples
async function demonstrateTextFileOperations() {
console.log('=== Web TypeScript Text File Read/Write Examples ===\n');
const fileSelector = new FileSelector();
const reader = new TextFileReader();
const writer = new TextFileWriter();
const fileInfo = new FileInfoHelper();
const validator = new FileValidator();
const batchOps = new BatchFileOperations();
const processor = new FileProcessingUtils();
const downloader = new FileDownloadHelper();
// 1. Select and read file
console.log('--- 1. Select and Read File ---');
try {
const file = await fileSelector.selectFile('.txt');
fileInfo.printFileInfo(file);
const content = await reader.readAsText(file);
console.log(`File content: ${content.substring(0, 100)}...`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 2. Write text file
console.log('\n--- 2. Write Text File ---');
writer.writeText('example.txt', 'Hello, World!\nThis is a test file.');
console.log('File written and downloaded');
// 3. Write multiple lines
console.log('\n--- 3. Write Lines ---');
const lines = ['Line 1', 'Line 2', 'Line 3', 'Line 4', 'Line 5'];
writer.writeLines('lines.txt', lines);
console.log('Lines file written and downloaded');
// 4. Read with encoding
console.log('\n--- 4. Read with Encoding ---');
try {
const file = await fileSelector.selectFile('.txt');
const contentUTF8 = await reader.readAsTextWithEncoding(file, 'UTF-8');
console.log(`UTF-8 content: ${contentUTF8.substring(0, 100)}...`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 5. Read as data URL
console.log('\n--- 5. Read as Data URL ---');
try {
const file = await fileSelector.selectFile('.txt');
const dataURL = await reader.readAsDataURL(file);
console.log(`Data URL: ${dataURL.substring(0, 100)}...`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 6. File validation
console.log('\n--- 6. File Validation ---');
try {
const file = await fileSelector.selectFile('*');
console.log(`Is text file: ${validator.isTextFile(file)}`);
console.log(`Valid size (< 10MB): ${validator.isValidSize(file, 10 * 1024 * 1024)}`);
validator.validate(file, {
maxSize: 10 * 1024 * 1024,
allowedExtensions: ['txt', 'md', 'json']
});
console.log('File validation passed');
} catch (error) {
console.error(`Validation error: ${(error as Error).message}`);
}
// 7. Batch operations
console.log('\n--- 7. Batch Operations ---');
try {
const files = await fileSelector.selectMultipleFiles('*');
console.log(`Total files: ${files.length}`);
console.log(`Total size: ${fileInfo.getReadableSize(batchOps.getTotalSize(files))}`);
const textFiles = batchOps.filterByType(files, 'text/plain');
console.log(`Text files: ${textFiles.length}`);
const sortedFiles = batchOps.sortByName(files);
console.log('Files sorted by name');
const groupedFiles = batchOps.groupByType(files);
console.log(`File types: ${groupedFiles.size}`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 8. File processing
console.log('\n--- 8. File Processing ---');
try {
const file = await fileSelector.selectFile('.txt');
const content = await reader.readAsText(file);
console.log(`Word count: ${processor.countWords(content)}`);
console.log(`Line count: ${processor.countLines(content)}`);
console.log(`Character count: ${processor.countCharacters(content)}`);
const matches = await processor.searchInFile(file, 'test');
console.log(`Search matches on lines: ${matches.join(', ')}`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 9. Download helpers
console.log('\n--- 9. Download Helpers ---');
downloader.downloadText('download.txt', 'Downloaded content');
downloader.downloadJSON('data.json', { name: 'Test', value: 123 });
console.log('\n=== All Text File Operations Completed ===');
}
// Export functions
export { TextFileReader, TextFileWriter, FileInfoHelper, FileSelector, FileValidator, BatchFileOperations, FileProcessingUtils, FileDownloadHelper };
export { demonstrateTextFileOperations };
💻 File Copy/Move typescript
🟡 intermediate
⭐⭐⭐
Copy and move files with progress tracking and error handling in browser environment
⏱️ 25 min
🏷️ typescript, web, file operations
Prerequisites:
Intermediate TypeScript, File API
// Web TypeScript File Copy/Move Examples
// Browser-based file operations using File API and Blob operations
// 1. File Copy Operations
class FileCopier {
// Copy file (creates a new blob with same content)
copy(file: File): Blob {
return new Blob([file], { type: file.type });
}
// Copy file with new name
copyAs(file: File, newName: string): File {
const blob = this.copy(file);
return new File([blob], newName, { type: file.type });
}
// Copy multiple files
copyMultiple(files: File[]): File[] {
return files.map(file => this.copyAs(file, file.name));
}
// Copy with metadata
copyWithMetadata(file: File, metadata: Record<string, any>): File {
const blob = this.copy(file);
const newName = metadata.newName || file.name;
const newFile = new File([blob], newName, {
type: file.type,
lastModified: metadata.lastModified || file.lastModified
});
return newFile;
}
// Clone file array
cloneFileList(files: FileList | File[]): File[] {
return Array.from(files).map(file => this.copyAs(file, file.name));
}
}
// 2. File Merge Operations
class FileMerger {
// Merge text files
async mergeTextFiles(files: File[], separator: string = '\n\n'): Promise<string> {
const reader = new TextFileReader();
const contents: string[] = [];
for (const file of files) {
const content = await reader.readAsText(file);
contents.push(content);
}
return contents.join(separator);
}
// Merge files as blob
async mergeAsBlob(files: File[], mimeType: string): Promise<Blob> {
const parts: BlobPart[] = [];
for (const file of files) {
const arrayBuffer = await file.arrayBuffer();
parts.push(arrayBuffer);
}
return new Blob(parts, { type: mimeType });
}
// Merge CSV files
async mergeCSVFiles(files: File[], skipHeaders: boolean = false): Promise<string> {
const reader = new TextFileReader();
let merged = '';
for (let i = 0; i < files.length; i++) {
const content = await reader.readAsText(files[i]);
const lines = content.split('\n');
let startLine = 0;
if (skipHeaders && i > 0) {
startLine = 1; // Skip header row
}
const relevantLines = lines.slice(startLine).join('\n');
merged += (i > 0 ? '\n' : '') + relevantLines;
}
return merged;
}
// Append file to another
async appendFile(baseFile: File, fileToAppend: File): Promise<File> {
const reader = new TextFileReader();
const baseContent = await reader.readAsText(baseFile);
const appendContent = await reader.readAsText(fileToAppend);
const merged = baseContent + '\n' + appendContent;
const blob = new Blob([merged], { type: baseFile.type });
return new File([blob], baseFile.name, { type: baseFile.type });
}
}
// 3. Batch File Operations
class BatchFileOperations {
// Batch rename files
batchRename(files: File[], newNamePattern: string, startIndex: number = 1): File[] {
const renamedFiles: File[] = [];
files.forEach((file, index) => {
const extension = file.name.split('.').pop();
const newName = newNamePattern.replace('{n}', String(startIndex + index));
const fullNewName = extension ? `${newName}.${extension}` : newName;
const blob = new Blob([file], { type: file.type });
const newFile = new File([blob], fullNewName, { type: file.type });
renamedFiles.push(newFile);
});
return renamedFiles;
}
// Batch change extension
batchChangeExtension(files: File[], newExtension: string): File[] {
return files.map(file => {
const nameWithoutExt = file.name.replace(/\.[^.]*$/, '');
const newName = `${nameWithoutExt}.${newExtension}`;
const blob = new Blob([file], { type: file.type });
return new File([blob], newName, { type: file.type });
});
}
// Add prefix to filenames
batchAddPrefix(files: File[], prefix: string): File[] {
return files.map(file => {
const newName = prefix + file.name;
const blob = new Blob([file], { type: file.type });
return new File([blob], newName, { type: file.type });
});
}
// Add suffix to filenames
batchAddSuffix(files: File[], suffix: string): File[] {
return files.map(file => {
const nameParts = file.name.split('.');
const name = nameParts.slice(0, -1).join('.');
const ext = nameParts.length > 1 ? nameParts[nameParts.length - 1] : '';
const newName = ext ? `${name}${suffix}.${ext}` : `${name}${suffix}`;
const blob = new Blob([file], { type: file.type });
return new File([blob], newName, { type: file.type });
});
}
}
// 4. File Transform Operations
class FileTransformer {
// Convert file to base64
async toBase64(file: File): Promise<string> {
const arrayBuffer = await file.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
let binary = '';
for (const byte of bytes) {
binary += String.fromCharCode(byte);
}
return btoa(binary);
}
// Convert base64 to file
base64ToFile(base64: string, fileName: string, mimeType: string): File {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
const blob = new Blob([bytes], { type: mimeType });
return new File([blob], fileName, { type: mimeType });
}
// Convert text file encoding
async convertEncoding(file: File, fromEncoding: string, toEncoding: string): Promise<File> {
const reader = new TextFileReader();
const text = await reader.readAsTextWithEncoding(file, fromEncoding);
const encoder = new TextEncoder();
const uint8Array = encoder.encode(text);
const blob = new Blob([uint8Array], { type: `text/plain;charset=${toEncoding}` });
return new File([blob], file.name, { type: blob.type });
}
// Compress file info (reduce metadata)
stripMetadata(file: File): File {
const blob = new Blob([file], { type: file.type });
return new File([blob], file.name, { type: file.type });
}
}
// 5. File Comparison
class FileComparer {
// Compare file sizes
compareBySize(file1: File, file2: File): number {
return file1.size - file2.size;
}
// Compare by name
compareByName(file1: File, file2: File): number {
return file1.name.localeCompare(file2.name);
}
// Compare by type
compareByType(file1: File, file2: File): number {
return file1.type.localeCompare(file2.type);
}
// Compare by date
compareByDate(file1: File, file2: File): number {
return file1.lastModified - file2.lastModified;
}
// Check if files are identical (by size and content)
async areIdentical(file1: File, file2: File): Promise<boolean> {
if (file1.size !== file2.size) {
return false;
}
const buffer1 = await file1.arrayBuffer();
const buffer2 = await file2.arrayBuffer();
const bytes1 = new Uint8Array(buffer1);
const bytes2 = new Uint8Array(buffer2);
for (let i = 0; i < bytes1.length; i++) {
if (bytes1[i] !== bytes2[i]) {
return false;
}
}
return true;
}
// Find duplicate files
async findDuplicates(files: File[]): Promise<Map<string, File[]>> {
const duplicates = new Map<string, File[]>();
const signatures = new Map<string, File[]>();
for (const file of files) {
// Use size and first 1KB as quick signature
const chunk = file.slice(0, 1024);
const arrayBuffer = await chunk.arrayBuffer();
const bytes = new Uint8Array(arrayBuffer);
const signature = `${file.size}-${Array.from(bytes.slice(0, 32)).join('-')}`;
if (!signatures.has(signature)) {
signatures.set(signature, []);
}
signatures.get(signature)!.push(file);
}
// Check potential duplicates with full comparison
for (const [signature, filesList] of signatures) {
if (filesList.length > 1) {
const confirmedDupes: File[] = [];
for (let i = 0; i < filesList.length; i++) {
for (let j = i + 1; j < filesList.length; j++) {
if (await this.areIdentical(filesList[i], filesList[j])) {
if (!confirmedDupes.includes(filesList[i])) {
confirmedDupes.push(filesList[i]);
}
if (!confirmedDupes.includes(filesList[j])) {
confirmedDupes.push(filesList[j]);
}
}
}
}
if (confirmedDupes.length > 1) {
duplicates.set(confirmedDupes[0].name, confirmedDupes);
}
}
}
return duplicates;
}
}
// 6. File Organization
class FileOrganizer {
// Organize files by type
organizeByType(files: File[]): Map<string, File[]> {
const organized = new Map<string, File[]>();
for (const file of files) {
const type = file.type.split('/')[0] || 'unknown';
if (!organized.has(type)) {
organized.set(type, []);
}
organized.get(type)!.push(file);
}
return organized;
}
// Organize by extension
organizeByExtension(files: File[]): Map<string, File[]> {
const organized = new Map<string, File[]>();
for (const file of files) {
const ext = file.name.split('.').pop()?.toLowerCase() || 'no-extension';
if (!organized.has(ext)) {
organized.set(ext, []);
}
organized.get(ext)!.push(file);
}
return organized;
}
// Organize by size range
organizeBySize(files: File[]): Map<string, File[]> {
const organized = new Map<string, File[]>();
const sizeRanges = [
{ name: 'Small (< 100KB)', max: 100 * 1024 },
{ name: 'Medium (100KB - 1MB)', min: 100 * 1024, max: 1024 * 1024 },
{ name: 'Large (1MB - 10MB)', min: 1024 * 1024, max: 10 * 1024 * 1024 },
{ name: 'Very Large (> 10MB)', min: 10 * 1024 * 1024 }
];
for (const file of files) {
for (const range of sizeRanges) {
if ((!range.min || file.size >= range.min) &&
(!range.max || file.size < range.max)) {
if (!organized.has(range.name)) {
organized.set(range.name, []);
}
organized.get(range.name)!.push(file);
break;
}
}
}
return organized;
}
// Organize by date
organizeByDate(files: File[]): Map<string, File[]> {
const organized = new Map<string, File[]>();
for (const file of files) {
const date = new Date(file.lastModified);
const dateKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
if (!organized.has(dateKey)) {
organized.set(dateKey, []);
}
organized.get(dateKey)!.push(file);
}
return organized;
}
}
// 7. File Operations with Progress
class FileOperationsWithProgress {
// Copy with progress callback
async copyWithProgress(file: File, onProgress: (progress: number) => void): Promise<Blob> {
const chunkSize = 1024 * 1024; // 1MB chunks
const chunks: BlobPart[] = [];
let offset = 0;
while (offset < file.size) {
const end = Math.min(offset + chunkSize, file.size);
const chunk = file.slice(offset, end);
const arrayBuffer = await chunk.arrayBuffer();
chunks.push(arrayBuffer);
offset = end;
const progress = Math.floor((offset / file.size) * 100);
onProgress(progress);
}
return new Blob(chunks, { type: file.type });
}
// Batch copy with progress
async batchCopyWithProgress(
files: File[],
onProgress: (current: number, total: number, file: File) => void
): Promise<File[]> {
const copiedFiles: File[] = [];
for (let i = 0; i < files.length; i++) {
const copied = await this.copyWithProgress(files[i], () => {});
const newFile = new File([copied], files[i].name, { type: files[i].type });
copiedFiles.push(newFile);
onProgress(i + 1, files.length, files[i]);
}
return copiedFiles;
}
}
// Import helper classes
class TextFileReader {
async readAsText(file: File): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsText(file);
});
}
async readAsTextWithEncoding(file: File, encoding: string): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result as string);
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsText(file, encoding);
});
}
}
// Usage Examples
async function demonstrateFileCopyMove() {
console.log('=== Web TypeScript File Copy/Move Examples ===\n');
const fileSelector = new FileSelector();
const copier = new FileCopier();
const merger = new FileMerger();
const batchOps = new BatchFileOperations();
const transformer = new FileTransformer();
const comparer = new FileComparer();
const organizer = new FileOrganizer();
const progressOps = new FileOperationsWithProgress();
// 1. Copy file
console.log('--- 1. Copy File ---');
try {
const file = await fileSelector.selectFile('*');
const copied = copier.copy(file);
console.log(`Copied file size: ${copied.size}`);
const copyAs = copier.copyAs(file, 'copy_' + file.name);
console.log(`Copied as: ${copyAs.name}`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 2. Merge files
console.log('\n--- 2. Merge Files ---');
try {
const files = await fileSelector.selectMultipleFiles('.txt');
const merged = await merger.mergeTextFiles(files);
console.log(`Merged content length: ${merged.length}`);
// Download merged file
const blob = new Blob([merged], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'merged.txt';
link.click();
URL.revokeObjectURL(url);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 3. Batch rename
console.log('\n--- 3. Batch Rename ---');
try {
const files = await fileSelector.selectMultipleFiles('*');
const renamed = batchOps.batchRename(files, 'file_{n}');
console.log('Renamed files:');
renamed.forEach(file => console.log(` - ${file.name}`));
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 4. Convert to base64
console.log('\n--- 4. Convert to Base64 ---');
try {
const file = await fileSelector.selectFile('image/*');
const base64 = await transformer.toBase64(file);
console.log(`Base64 length: ${base64.length}`);
console.log(`Base64 preview: ${base64.substring(0, 100)}...`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 5. Find duplicates
console.log('\n--- 5. Find Duplicates ---');
try {
const files = await fileSelector.selectMultipleFiles('*');
const duplicates = await comparer.findDuplicates(files);
console.log(`Found ${duplicates.size} sets of duplicates`);
for (const [name, dupes] of duplicates) {
console.log(` - ${name}: ${dupes.length} copies`);
}
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 6. Organize by type
console.log('\n--- 6. Organize by Type ---');
try {
const files = await fileSelector.selectMultipleFiles('*');
const organized = organizer.organizeByType(files);
for (const [type, filesList] of organized) {
console.log(`${type}: ${filesList.length} files`);
}
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 7. Copy with progress
console.log('\n--- 7. Copy with Progress ---');
try {
const file = await fileSelector.selectFile('*');
const copied = await progressOps.copyWithProgress(file, (progress) => {
console.log(`Progress: ${progress}%`);
});
console.log(`Copy completed. Size: ${copied.size}`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
console.log('\n=== All File Copy/Move Examples Completed ===');
}
class FileSelector {
async selectFile(accept: string): Promise<File> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = accept;
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(files[0]);
} else {
reject(new Error('No file selected'));
}
};
input.click();
});
}
async selectMultipleFiles(accept: string): Promise<File[]> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.accept = accept;
input.multiple = true;
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(Array.from(files));
} else {
reject(new Error('No files selected'));
}
};
input.click();
});
}
}
// Export functions
export { FileCopier, FileMerger, BatchFileOperations, FileTransformer, FileComparer, FileOrganizer, FileOperationsWithProgress };
export { demonstrateFileCopyMove };
💻 Directory Traversal typescript
🔴 complex
⭐⭐⭐⭐
Traverse directories using webkitdirectory and FileSystem API
⏱️ 30 min
🏷️ typescript, web, file operations, directory
Prerequisites:
Advanced TypeScript, FileSystem API
// Web TypeScript Directory Traversal Examples
// Using webkitDirectory, FileSystemDirectoryHandle, and FileSystem API
// 1. Directory Selection
class DirectorySelector {
// Select directory using webkitdirectory
async selectDirectory(): Promise<File[]> {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.webkitdirectory = true;
input.multiple = true;
input.onchange = (e) => {
const files = (e.target as HTMLInputElement).files;
if (files && files.length > 0) {
resolve(Array.from(files));
} else {
reject(new Error('No files selected'));
}
};
input.oncancel = () => {
reject(new Error('Directory selection cancelled'));
};
input.click();
});
}
// Select directory with FileSystem API (modern browsers)
async selectDirectoryHandle(): Promise<FileSystemDirectoryHandle> {
if ('showDirectoryPicker' in window) {
const handle = await (window as any).showDirectoryPicker();
return handle;
} else {
throw new Error('FileSystem API not supported');
}
}
}
// 2. Directory Traversal
class DirectoryTraverser {
// Build directory tree from files
buildDirectoryTree(files: File[]): DirectoryNode {
const root = new DirectoryNode('root', '/', true);
for (const file of files) {
const pathParts = file.webkitRelativePath.split('/');
let currentNode = root;
for (let i = 0; i < pathParts.length - 1; i++) {
const part = pathParts[i];
let child = currentNode.children.find(c => c.name === part);
if (!child) {
child = new DirectoryNode(part, pathParts.slice(0, i + 1).join('/'), true);
currentNode.children.push(child);
}
currentNode = child;
}
// Add file node
const fileName = pathParts[pathParts.length - 1];
currentNode.children.push(new DirectoryNode(fileName, file.webkitRelativePath, false, file));
}
return root;
}
// Flatten directory tree
flattenDirectoryTree(node: DirectoryNode, path: string = ''): File[] {
const files: File[] = [];
for (const child of node.children) {
const childPath = path ? `${path}/${child.name}` : child.name;
if (child.isDirectory) {
files.push(...this.flattenDirectoryTree(child, childPath));
} else if (child.file) {
files.push(child.file);
}
}
return files;
}
// Print directory tree
printDirectoryTree(node: DirectoryNode, indent: number = 0): void {
const prefix = ' '.repeat(indent);
const icon = node.isDirectory ? '📁' : '📄';
console.log(`${icon} ${prefix}${node.name}`);
for (const child of node.children) {
this.printDirectoryTree(child, indent + 1);
}
}
// Get directory statistics
getDirectoryStats(node: DirectoryNode): DirectoryStats {
const stats = new DirectoryStats();
this.calculateStats(node, stats);
return stats;
}
private calculateStats(node: DirectoryNode, stats: DirectoryStats): void {
if (node.isDirectory) {
stats.directoryCount++;
for (const child of node.children) {
this.calculateStats(child, stats);
}
} else {
stats.fileCount++;
stats.totalSize += node.file?.size || 0;
}
}
// Find files by name
findByName(node: DirectoryNode, name: string, recursive: boolean = true): DirectoryNode[] {
const results: DirectoryNode[] = [];
this.searchByName(node, name, results, recursive);
return results;
}
private searchByName(node: DirectoryNode, name: string, results: DirectoryNode[], recursive: boolean): void {
for (const child of node.children) {
if (child.name.toLowerCase().includes(name.toLowerCase())) {
results.push(child);
}
if (recursive && child.isDirectory) {
this.searchByName(child, name, results, recursive);
}
}
}
// Find files by extension
findByExtension(node: DirectoryNode, extensions: string[]): DirectoryNode[] {
const results: DirectoryNode[] = [];
this.searchByExtension(node, extensions, results);
return results;
}
private searchByExtension(node: DirectoryNode, extensions: string[], results: DirectoryNode[]): void {
for (const child of node.children) {
if (!child.isDirectory && child.file) {
const ext = child.name.split('.').pop()?.toLowerCase();
if (ext && extensions.includes(ext)) {
results.push(child);
}
}
if (child.isDirectory) {
this.searchByExtension(child, extensions, results);
}
}
}
// Filter files by size
filterBySize(node: DirectoryNode, minSize: number, maxSize: number): DirectoryNode[] {
const results: DirectoryNode[] = [];
this.searchBySize(node, minSize, maxSize, results);
return results;
}
private searchBySize(node: DirectoryNode, minSize: number, maxSize: number, results: DirectoryNode[]): void {
for (const child of node.children) {
if (!child.isDirectory && child.file) {
const size = child.file.size;
if (size >= minSize && size <= maxSize) {
results.push(child);
}
}
if (child.isDirectory) {
this.searchBySize(child, minSize, maxSize, results);
}
}
}
}
// 3. File System API Operations (Modern Browsers)
class FileSystemOperations {
// Read directory contents using FileSystem API
async readDirectory(handle: FileSystemDirectoryHandle): Promise<Map<string, FileSystemHandle>> {
const entries = new Map<string, FileSystemHandle>();
for await (const entry of handle.values()) {
entries.set(entry.name, entry);
}
return entries;
}
// Recursively read directory
async readDirectoryRecursively(handle: FileSystemDirectoryHandle, path: string = ''): Promise<Map<string, FileSystemHandle>> {
const entries = new Map<string, FileSystemHandle>();
for await (const entry of handle.values()) {
const entryPath = path ? `${path}/${entry.name}` : entry.name;
entries.set(entryPath, entry);
if (entry.kind === 'directory') {
const dirHandle = entry as FileSystemDirectoryHandle;
const subEntries = await this.readDirectoryRecursively(dirHandle, entryPath);
for (const [subPath, subEntry] of subEntries) {
entries.set(subPath, subEntry);
}
}
}
return entries;
}
// Get file from directory
async getFile(handle: FileSystemDirectoryHandle, fileName: string): Promise<File> {
const fileHandle = await handle.getFileHandle(fileName);
const file = await fileHandle.getFile();
return file;
}
// Get all files from directory
async getAllFiles(handle: FileSystemDirectoryHandle): Promise<File[]> {
const files: File[] = [];
for await (const entry of handle.values()) {
if (entry.kind === 'file') {
const fileHandle = entry as FileSystemFileHandle;
const file = await fileHandle.getFile();
files.push(file);
} else if (entry.kind === 'directory') {
const dirHandle = entry as FileSystemDirectoryHandle;
const subFiles = await this.getAllFiles(dirHandle);
files.push(...subFiles);
}
}
return files;
}
// Create directory
async createDirectory(handle: FileSystemDirectoryHandle, dirName: string): Promise<FileSystemDirectoryHandle> {
return await handle.getDirectoryHandle(dirName, { create: true });
}
// Create file
async createFile(handle: FileSystemDirectoryHandle, fileName: string, content: string): Promise<void> {
const fileHandle = await handle.getFileHandle(fileName, { create: true });
const writable = await fileHandle.createWritable();
await writable.write(content);
await writable.close();
}
// Delete file
async deleteFile(handle: FileSystemDirectoryHandle, fileName: string): Promise<void> {
await handle.removeEntry(fileName);
}
// Delete directory
async deleteDirectory(handle: FileSystemDirectoryHandle, dirName: string): Promise<void> {
await handle.removeEntry(dirName, { recursive: true });
}
}
// 4. Directory Tree Structure
class DirectoryNode {
name: string;
path: string;
isDirectory: boolean;
file?: File;
children: DirectoryNode[];
constructor(name: string, path: string, isDirectory: boolean, file?: File) {
this.name = name;
this.path = path;
this.isDirectory = isDirectory;
this.file = file;
this.children = [];
}
// Sort children by name
sortByName(): void {
this.children.sort((a, b) => a.name.localeCompare(b.name));
for (const child of this.children) {
if (child.isDirectory) {
child.sortByName();
}
}
}
// Sort children by size (files only)
sortBySize(): void {
this.children.sort((a, b) => {
if (a.isDirectory && !b.isDirectory) return -1;
if (!a.isDirectory && b.isDirectory) return 1;
if (!a.isDirectory && !b.isDirectory) {
return (a.file?.size || 0) - (b.file?.size || 0);
}
return 0;
});
for (const child of this.children) {
if (child.isDirectory) {
child.sortBySize();
}
}
}
// Filter by predicate
filter(predicate: (node: DirectoryNode) => boolean): DirectoryNode | null {
if (!predicate(this)) {
return null;
}
const filteredNode = new DirectoryNode(this.name, this.path, this.isDirectory, this.file);
for (const child of this.children) {
const filteredChild = child.filter(predicate);
if (filteredChild) {
filteredNode.children.push(filteredChild);
}
}
return filteredNode;
}
}
// 5. Directory Statistics
class DirectoryStats {
fileCount: number = 0;
directoryCount: number = 0;
totalSize: number = 0;
extensionCounts: Map<string, number> = new Map();
// Get largest files
getLargestFiles(node: DirectoryNode, count: number = 10): DirectoryNode[] {
const files: DirectoryNode[] = [];
this.collectFiles(node, files);
return files
.sort((a, b) => (b.file?.size || 0) - (a.file?.size || 0))
.slice(0, count);
}
private collectFiles(node: DirectoryNode, files: DirectoryNode[]): void {
for (const child of node.children) {
if (child.isDirectory) {
this.collectFiles(child, files);
} else {
files.push(child);
}
}
}
// Get extension statistics
getExtensionStats(): Map<string, number> {
return new Map([...this.extensionCounts.entries()].sort((a, b) => b[1] - a[1]));
}
// Print statistics
print(): void {
console.log('\n=== Directory Statistics ===');
console.log(`Files: ${this.fileCount}`);
console.log(`Directories: ${this.directoryCount}`);
console.log(`Total Size: ${this.formatBytes(this.totalSize)}`);
console.log('\nExtension Distribution:');
const extStats = this.getExtensionStats();
for (const [ext, count] of extStats) {
console.log(` .${ext}: ${count}`);
}
}
private formatBytes(bytes: number): string {
const kb = bytes / 1024;
const mb = kb / 1024;
const gb = mb / 1024;
if (gb >= 1) {
return `${gb.toFixed(2)} GB`;
} else if (mb >= 1) {
return `${mb.toFixed(2)} MB`;
} else if (kb >= 1) {
return `${kb.toFixed(2)} KB`;
} else {
return `${bytes} bytes`;
}
}
}
// 6. Advanced Directory Operations
class AdvancedDirectoryOperations {
private traverser = new DirectoryTraverser();
private fsOps = new FileSystemOperations();
// Synchronize directories
async syncDirectories(source: FileSystemDirectoryHandle, target: FileSystemDirectoryHandle): Promise<void> {
const sourceEntries = await this.fsOps.readDirectoryRecursively(source);
const targetEntries = await this.fsOps.readDirectoryRecursively(target);
for (const [path, entry] of sourceEntries) {
if (entry.kind === 'file') {
const sourceFile = entry as FileSystemFileHandle;
const file = await sourceFile.getFile();
if (!targetEntries.has(path)) {
// Create file in target
const pathParts = path.split('/');
const fileName = pathParts[pathParts.length - 1];
let targetDir = target;
// Navigate/create subdirectories
for (let i = 0; i < pathParts.length - 1; i++) {
targetDir = await this.fsOps.createDirectory(targetDir, pathParts[i]);
}
// Copy file content
const content = await file.text();
await this.fsOps.createFile(targetDir, fileName, content);
}
}
}
}
// Backup directory
async backupDirectory(handle: FileSystemDirectoryHandle): Promise<Blob> {
const files = await this.fsOps.getAllFiles(handle);
const parts: BlobPart[] = [];
for (const file of files) {
const arrayBuffer = await file.arrayBuffer();
parts.push(arrayBuffer);
}
return new Blob(parts, { type: 'application/zip' });
}
// Search in directory
async searchInDirectory(
handle: FileSystemDirectoryHandle,
searchText: string,
fileExtensions: string[] = ['txt', 'md', 'json']
): Promise<Map<string, string[]>> {
const results = new Map<string, string[]>();
for await (const entry of handle.values()) {
if (entry.kind === 'file') {
const ext = entry.name.split('.').pop()?.toLowerCase();
if (ext && fileExtensions.includes(ext)) {
const fileHandle = entry as FileSystemFileHandle;
const file = await fileHandle.getFile();
const content = await file.text();
if (content.toLowerCase().includes(searchText.toLowerCase())) {
const lines = content.split('\n');
const matches: string[] = [];
lines.forEach((line, index) => {
if (line.toLowerCase().includes(searchText.toLowerCase())) {
matches.push(`Line ${index + 1}: ${line.trim()}`);
}
});
results.set(file.name, matches);
}
}
} else if (entry.kind === 'directory') {
const dirHandle = entry as FileSystemDirectoryHandle;
const subResults = await this.searchInDirectory(dirHandle, searchText, fileExtensions);
for (const [fileName, matches] of subResults) {
results.set(`${entry.name}/${fileName}`, matches);
}
}
}
return results;
}
}
// Usage Examples
async function demonstrateDirectoryTraversal() {
console.log('=== Web TypeScript Directory Traversal Examples ===\n');
const dirSelector = new DirectorySelector();
const traverser = new DirectoryTraverser();
const fsOps = new FileSystemOperations();
const advancedOps = new AdvancedDirectoryOperations();
// 1. Select directory (webkitdirectory)
console.log('--- 1. Select Directory (webkitdirectory) ---');
try {
const files = await dirSelector.selectDirectory();
console.log(`Selected ${files.length} files`);
// Build directory tree
const tree = traverser.buildDirectoryTree(files);
traverser.printDirectoryTree(tree);
// Get statistics
const stats = traverser.getDirectoryStats(tree);
stats.print();
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 2. Select directory with FileSystem API
console.log('\n--- 2. Select Directory (FileSystem API) ---');
try {
const handle = await dirSelector.selectDirectoryHandle();
console.log(`Selected directory handle`);
const entries = await fsOps.readDirectory(handle);
console.log(`Directory contents: ${entries.size} entries`);
for (const [name, entry] of entries) {
const icon = entry.kind === 'directory' ? '📁' : '📄';
console.log(` ${icon} ${name}`);
}
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 3. Recursive directory read
console.log('\n--- 3. Recursive Directory Read ---');
try {
const handle = await dirSelector.selectDirectoryHandle();
const allEntries = await fsOps.readDirectoryRecursively(handle);
console.log(`Total entries (recursive): ${allEntries.size}`);
// Print first 20 entries
let count = 0;
for (const [path, entry] of allEntries) {
if (count++ < 20) {
const icon = entry.kind === 'directory' ? '📁' : '📄';
console.log(` ${icon} ${path}`);
}
}
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 4. Get all files
console.log('\n--- 4. Get All Files ---');
try {
const handle = await dirSelector.selectDirectoryHandle();
const files = await fsOps.getAllFiles(handle);
console.log(`Total files: ${files.length}`);
console.log(`Total size: ${files.reduce((sum, f) => sum + f.size, 0)} bytes`);
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
// 5. Search in directory
console.log('\n--- 5. Search in Directory ---');
try {
const handle = await dirSelector.selectDirectoryHandle();
const results = await advancedOps.searchInDirectory(handle, 'function');
console.log(`Search results in ${results.size} files`);
for (const [fileName, matches] of results) {
console.log(` ${fileName}: ${matches.length} matches`);
matches.slice(0, 3).forEach(match => {
console.log(` ${match}`);
});
}
} catch (error) {
console.error(`Error: ${(error as Error).message}`);
}
console.log('\n=== All Directory Traversal Examples Completed ===');
}
// Export functions
export { DirectorySelector, DirectoryTraverser, FileSystemOperations, DirectoryNode, DirectoryStats, AdvancedDirectoryOperations };
export { demonstrateDirectoryTraversal };