圖片轉換器技術實作

深入了解混合架構圖片轉換器的完整實作,包含前端預覽和後端處理

概述

圖片轉換器是一個專業的混合架構圖片處理工具,結合了前端預覽和後端處理的優勢。 支援 JPG、PNG、WEBP、BMP 等主流格式互轉,具備批次上傳、即時預覽、品質調整、自動清理等功能。 採用 Python 後端 + JavaScript 前端的分離式架構,確保高效能處理和使用者體驗。

🔒 混合架構優勢

前端負責預覽和互動,後端負責圖片處理,檔案處理後自動清理,確保伺服器資源高效利用和使用者隱私保護。

技術架構與核心設計

混合架構設計

技術層面 實作方式 設計考量
前端架構 ES6+ JavaScript + HTML5 Canvas 即時預覽、拖拽上傳、品質調整
後端處理 Python + Pillow (PIL) 高效能圖片處理、格式轉換
檔案管理 臨時檔案系統 + 自動清理 2小時自動清理,防止儲存空間溢出
API 設計 RESTful API + JSON 回應 標準化介面,易於維護和擴展
品質控制 動態品質調整 + 即時預覽 平衡檔案大小與圖片品質

系統架構流程

/**
 * 圖片轉換器系統架構流程
 * Image Converter System Architecture Flow
 */

前端 (Frontend)                    後端 (Backend)
┌─────────────────────────────────┐  ┌─────────────────────────────────┐
│   1. 檔案選擇/拖拽上傳            │  │   Flask API Server              │
│   2. 前端預覽和驗證              │  │   ┌─────────────────────────────┐ │
│   3. 品質/格式設定               │  │   │  ImageConverter Tool        │ │
│   4. 批次檔案管理               │  │   │  ┌─────────────────────────┐ │ │
│   5. 即時狀態更新               │  │   │  │   Pillow (PIL)         │ │ │
│   6. 結果下載和清理             │  │   │  │   Format Conversion    │ │ │
└─────────────────────────────────┘  │   │  │   Quality Adjustment   │ │ │
                                     │   │  │   Batch Processing     │ │ │
           ↕ HTTP API 通信           │   │  └─────────────────────────┘ │ │
                                     │   └─────────────────────────────┘ │
┌─────────────────────────────────┐  │   ┌─────────────────────────────┐ │
│   File Upload API               │  │   │  Temporary File System      │ │
│   POST /api/image-converter/    │  │   │  ┌─────────────────────────┐ │ │
│   upload                        │  │   │  │   uploads/              │ │ │
│                                 │  │   │  │   converted/            │ │ │
│   Convert API                   │  │   │  │   Auto cleanup (2h)    │ │ │
│   POST /api/image-converter/    │  │   │  └─────────────────────────┘ │ │
│   convert                       │  │   └─────────────────────────────┘ │
│                                 │  │                                   │
│   Download API                  │  │   ┌─────────────────────────────┐ │
│   GET /api/image-converter/     │  │   │  Error Handling &           │ │
│   download/{file_id}            │  │   │  Logging System             │ │
└─────────────────────────────────┘  │   └─────────────────────────────┘ │
                                     └─────────────────────────────────┘

核心功能特色

1. 多格式支援與轉換

📸 支援格式

  • JPEG/JPG - 最常用的有損壓縮格式
  • PNG - 無損壓縮,支援透明度
  • WEBP - 現代高效壓縮格式
  • BMP - 未壓縮點陣圖格式

⚡ 批次處理

  • 最多同時處理 5 張圖片
  • 拖拽上傳,支援多選
  • 即時預覽和進度追蹤
  • 統一設定,批次轉換

🎛️ 品質控制

  • 動態品質調整滑桿 (10-100)
  • 即時檔案大小預覽
  • 智能預設值建議
  • 平衡品質與檔案大小

2. 自動化檔案管理

# 自動清理系統 - Auto Cleanup System
def cleanup_old_files():
    """定期清理過期檔案 / Regularly clean up expired files"""
    while True:
        try:
            current_time = datetime.now()
            cleanup_hours = 2  # 2小時後清理 / Clean up after 2 hours
            
            # 清理資料夾 / Cleanup folders
            cleanup_folders = ['uploads', 'converted']
            
            for folder in cleanup_folders:
                if not os.path.exists(folder):
                    continue
                    
                for filename in os.listdir(folder):
                    file_path = os.path.join(folder, filename)
                    if os.path.isfile(file_path):
                        file_time = datetime.fromtimestamp(os.path.getctime(file_path))
                        if current_time - file_time > timedelta(hours=cleanup_hours):
                            os.remove(file_path)
                            logger.info(f"清理過期檔案: {filename}")
        except Exception as e:
            logger.error(f"清理檔案時發生錯誤: {e}")
        
        time.sleep(3600)  # 每小時檢查一次 / Check every hour

完整程式碼實作

以下是圖片轉換器的完整程式碼實作,按功能模組分類展示。點擊卡片標題可展開查看詳細程式碼:

📦 核心圖片轉換器類別

功能:主要的 ImageConverter 類別,包含建構函數、初始化方法和核心配置

/**
 * Image Converter - Core Implementation
 * 圖片轉換器 - 核心實作
 * 
 * Professional image conversion tool with hybrid architecture
 * 專業的混合架構圖片轉換工具
 */
class ImageConverter {
    constructor() {
        // 檔案管理 / File management
        this.files = [];                // 上傳的檔案清單
        this.convertedFiles = [];       // 轉換後的檔案清單
        this.maxFiles = 5;             // 最大檔案數量限制
        
        // 語言設定 / Language settings
        this.currentLanguage = 'zh';    // 當前語言設定
        
        // 轉換設定 / Conversion settings
        this.defaultQuality = 80;       // 預設品質設定
        this.supportedFormats = ['jpeg', 'jpg', 'png', 'webp', 'bmp'];
        
        // 狀態管理 / State management
        this.isUploading = false;       // 上傳狀態標記
        this.isConverting = false;      // 轉換狀態標記
        
        // 翻譯字典 / Translation dictionary
        this.translations = {
            zh: {
                upload: '上傳圖片',
                convert: '開始轉換',
                download: '下載',
                quality: '品質',
                format: '格式',
                maxFiles: '最多上傳5張圖片',
                uploading: '上傳中...',
                converting: '轉換中...',
                success: '轉換成功',
                error: '轉換失敗',
                unsupportedFormat: '不支援的檔案格式',
                fileTooLarge: '檔案大小超過5MB限制',
                uploadFailed: '上傳失敗',
                conversionFailed: '轉換失敗',
                downloadFailed: '下載失敗',
                batchDownloadFailed: '批次下載失敗',
                noFiles: '沒有可轉換的檔案',
                noConvertedFiles: '沒有可下載的檔案',
                processingResults: '處理結果',
                conversionCompleted: '轉換完成!',
                convertTo: '轉換為:',
                remove: '移除',
                batchSetFormat: '批量設定轉換格式',
                convertAllTo: '將所有圖片轉換為:',
                selectFormat: '選擇格式',
                batchDownloadZip: '批次下載 ZIP'
            },
            en: {
                upload: 'Upload Images',
                convert: 'Start Conversion',
                download: 'Download',
                quality: 'Quality',
                format: 'Format',
                maxFiles: 'Maximum 5 images allowed',
                uploading: 'Uploading...',
                converting: 'Converting...',
                success: 'Conversion successful',
                error: 'Conversion failed',
                unsupportedFormat: 'Unsupported file format',
                fileTooLarge: 'File size exceeds 5MB limit',
                uploadFailed: 'Upload failed',
                conversionFailed: 'Conversion failed',
                downloadFailed: 'Download failed',
                batchDownloadFailed: 'Batch download failed',
                noFiles: 'No files to convert',
                noConvertedFiles: 'No files available for download',
                processingResults: 'Processing results',
                conversionCompleted: 'Conversion completed!',
                convertTo: 'Convert to:',
                remove: 'Remove',
                batchSetFormat: 'Batch Set Format',
                convertAllTo: 'Convert all images to:',
                selectFormat: 'Select format',
                batchDownloadZip: 'Download All as ZIP'
            }
        };
        
        // 啟動初始化 / Initialize
        this.init();
    }
    
    /**
     * 初始化方法 - 按順序執行所有初始化步驟
     * Initialization method - Execute all initialization steps in order
     */
    init() {
        this.setupGlobalLanguageListener(); // 必須最先執行 / Must execute first
        this.setupEventListeners();        // 設置事件監聽 / Setup event listeners
        this.setupDragAndDrop();          // 設置拖拽功能 / Setup drag & drop
        this.updateLanguage();            // 更新語言界面 / Update language interface
        
        console.log('圖片轉換器初始化完成 / Image converter initialized');
    }
    
    /**
     * 獲取翻譯文字 - 根據當前語言返回對應翻譯
     * Get translation text - Return corresponding translation based on current language
     */
    getTranslation(key) {
        return this.translations[this.currentLanguage][key] || key;
    }
    
    /**
     * 格式化檔案大小 - 將位元組轉換為易讀格式
     * Format file size - Convert bytes to readable format
     */
    formatFileSize(bytes) {
        if (bytes === 0) return '0 Bytes';
        const k = 1024;
        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    }
    
    /**
     * 獲取預設輸出格式 - 根據輸入格式智能選擇輸出格式
     * Get default output format - Smart selection based on input format
     */
    getDefaultOutputFormat(inputType) {
        const typeMap = {
            'image/jpeg': 'png',
            'image/jpg': 'png',
            'image/png': 'jpeg',
            'image/webp': 'png',
            'image/bmp': 'png'
        };
        return typeMap[inputType] || 'png';
    }
    
    /**
     * 生成轉換後檔案名稱
     * Generate converted file name
     */
    generateConvertedName(originalName, format) {
        const nameWithoutExt = originalName.replace(/\.[^/.]+$/, "");
        const ext = format === 'jpeg' ? 'jpg' : format;
        return `${nameWithoutExt}_converted.${ext}`;
    }
}

🌐 全域語言系統整合

功能:整合 Tool Master 全域語言切換系統,支援即時雙語切換

/**
 * 設置全域語言監聽器
 * Setup global language listener
 * 
 * 這是工具與 Tool Master 語言系統整合的關鍵方法
 * This is the key method for tool integration with Tool Master language system
 */
setupGlobalLanguageListener() {
    // 監聽全域語言切換事件 / Listen to global language change events
    window.addEventListener('languageChanged', (event) => {
        this.currentLanguage = event.detail.language;
        this.updateLanguage();
        console.log(`圖片轉換器語言已切換至: ${this.currentLanguage}`);
    });
    
    // 處理全域語言系統加載時序問題 / Handle global language system loading timing
    if (window.globalI18n) {
        this.currentLanguage = window.globalI18n.currentLanguage;
        console.log(`從全域語言系統獲取語言: ${this.currentLanguage}`);
    }
}

/**
 * 更新語言界面
 * Update language interface
 * 
 * 根據當前語言設定更新所有界面元素
 * Update all interface elements based on current language setting
 */
updateLanguage() {
    // 更新所有帶有 data-zh 和 data-en 屬性的元素
    const elements = document.querySelectorAll('[data-zh][data-en]');
    elements.forEach(element => {
        const text = element.getAttribute(`data-${this.currentLanguage}`);
        if (text) {
            element.textContent = text;
        }
    });
    
    // 重新渲染檔案列表以更新動態內容
    if (this.files.length > 0) {
        this.renderFilesList();
    }
    
    // 更新轉換結果列表
    if (this.convertedFiles.length > 0) {
        this.showResults();
    }
    
    // 更新上傳區域內容
    this.updateUploadAreaContent();
}

/**
 * 更新上傳區域內容
 * Update upload area content
 */
updateUploadAreaContent() {
    const uploadArea = document.getElementById('uploadArea');
    if (uploadArea && !this.isUploading) {
        const uploadText = uploadArea.querySelector('.upload-text');
        const uploadHint = uploadArea.querySelector('.upload-hint');
        
        if (uploadText) {
            uploadText.textContent = this.currentLanguage === 'zh' ? 
                '點擊上傳圖片或拖拽到此處' : 
                'Click to upload images or drag & drop here';
        }
        
        if (uploadHint) {
            uploadHint.textContent = this.currentLanguage === 'zh' ? 
                '支援 JPG、PNG、WEBP、BMP 格式,最多 5 張,每張最大 5MB' : 
                'Supports JPG, PNG, WEBP, BMP formats, up to 5 files, 5MB each';
        }
    }
}

/**
 * 顯示本地化錯誤訊息
 * Show localized error message
 */
showError(message) {
    const errorDiv = document.createElement('div');
    errorDiv.className = 'error-message';
    errorDiv.textContent = message;
    
    const container = document.querySelector('.container');
    container.insertBefore(errorDiv, container.children[2]);
    
    // 5秒後自動移除
    setTimeout(() => {
        errorDiv.remove();
    }, 5000);
}

/**
 * 顯示本地化成功訊息
 * Show localized success message
 */
showSuccess(message) {
    const successDiv = document.createElement('div');
    successDiv.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: #28a745;
        color: white;
        padding: 15px 20px;
        border-radius: 4px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
        z-index: 1000;
        animation: slideIn 0.3s ease-out;
        max-width: 300px;
        font-size: 14px;
        line-height: 1.4;
    `;
    successDiv.textContent = message;
    document.body.appendChild(successDiv);
    
    // 5秒後自動移除
    setTimeout(() => {
        successDiv.style.animation = 'slideOut 0.3s ease-out';
        setTimeout(() => successDiv.remove(), 300);
    }, 5000);
}

/**
 * 顯示上傳成功訊息
 * Show upload success message
 */
showUploadSuccess(fileCount) {
    const successMsg = this.currentLanguage === 'zh' ? 
        `成功上傳 ${fileCount} 張圖片!檔案已暫存,將在1小時後自動清理。` : 
        `Successfully uploaded ${fileCount} images! Files are temporarily stored and will be auto-cleaned after 1 hour.`;
    
    this.showSuccess(successMsg);
}

/**
 * 應用批次格式設定的本地化回饋
 * Apply batch format setting with localized feedback
 */
applyBatchFormatFeedback(format) {
    const successMsg = this.currentLanguage === 'zh' ? 
        `已將所有圖片的轉換格式設定為 ${format.toUpperCase()}` : 
        `All images will be converted to ${format.toUpperCase()}`;
    
    // 創建藍色提示(表示資訊性訊息)
    const infoDiv = document.createElement('div');
    infoDiv.style.cssText = `
        position: fixed;
        top: 20px;
        right: 20px;
        background: #17a2b8;
        color: white;
        padding: 12px 20px;
        border-radius: 4px;
        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
        z-index: 1000;
        animation: slideIn 0.3s ease-out;
        max-width: 300px;
        font-size: 14px;
    `;
    infoDiv.textContent = successMsg;
    document.body.appendChild(infoDiv);
    
    // 3秒後自動移除
    setTimeout(() => {
        infoDiv.style.animation = 'slideOut 0.3s ease-out';
        setTimeout(() => infoDiv.remove(), 300);
    }, 3000);
}

📤 檔案上傳與驗證系統

功能:處理檔案上傳、格式驗證、大小檢查和拖拽功能

/**
 * 檔案選擇處理
 * Handle file selection
 * 
 * 處理用戶選擇的檔案,包括驗證和上傳
 * Process user selected files including validation and upload
 */
async handleFileSelect(event) {
    const files = Array.from(event.target.files);
    
    // 檢查檔案數量限制 / Check file count limit
    if (this.files.length + files.length > this.maxFiles) {
        this.showError(this.getTranslation('maxFiles'));
        return;
    }
    
    // 顯示上傳進度 / Show upload progress
    this.showUploadProgress();
    
    try {
        // 建立 FormData 上傳到後端 / Create FormData for backend upload
        const formData = new FormData();
        files.forEach(file => {
            if (this.validateFile(file)) {
                formData.append('files', file);
            }
        });
        
        // 上傳到後端 / Upload to backend
        const response = await fetch('/api/image-converter/upload', {
            method: 'POST',
            body: formData
        });
        
        if (!response.ok) {
            throw new Error(`上傳失敗: ${response.status}`);
        }
        
        const result = await response.json();
        
        // 處理上傳成功的檔案 / Process successfully uploaded files
        for (const uploadedFile of result.files) {
            const fileObj = {
                id: uploadedFile.file_id,
                originalName: uploadedFile.original_name,
                size: uploadedFile.size,
                type: `image/${uploadedFile.format}`,
                outputFormat: this.getDefaultOutputFormat(`image/${uploadedFile.format}`),
                fileId: uploadedFile.file_id,
                uploaded: true
            };
            
            // 為後端檔案創建預覽
            await this.createPreviewFromBackend(fileObj);
            this.files.push(fileObj);
        }
        
        // 顯示上傳成功訊息
        this.showUploadSuccess(result.files.length);
        
        this.hideUploadProgress();
        this.updateUI();
        
    } catch (error) {
        console.error('上傳失敗:', error);
        this.hideUploadProgress();
        this.showError(this.currentLanguage === 'zh' ? 
            `上傳失敗: ${error.message}` : 
            `Upload failed: ${error.message}`);
    }
}

/**
 * 檔案驗證
 * File validation
 * 
 * 驗證檔案格式、大小和完整性
 * Validate file format, size, and integrity
 */
validateFile(file) {
    const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/bmp'];
    const maxSize = 5 * 1024 * 1024; // 5MB
    
    // 檢查檔案類型 / Check file type
    if (!validTypes.includes(file.type)) {
        this.showError(this.getTranslation('unsupportedFormat'));
        return false;
    }
    
    // 檢查檔案大小 / Check file size
    if (file.size > maxSize) {
        this.showError(this.getTranslation('fileTooLarge'));
        return false;
    }
    
    // 檢查檔案名稱是否有效
    if (!file.name || file.name.length === 0) {
        this.showError(this.currentLanguage === 'zh' ? 
            '檔案名稱無效' : 
            'Invalid file name');
        return false;
    }
    
    return true;
}

/**
 * 設置拖拽功能
 * Setup drag and drop functionality
 * 
 * 實現檔案拖拽上傳功能
 * Implement file drag and drop upload functionality
 */
setupDragAndDrop() {
    const uploadArea = document.getElementById('uploadArea');
    
    // 防止瀏覽器預設行為 / Prevent browser default behavior
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        uploadArea.addEventListener(eventName, (e) => {
            e.preventDefault();
            e.stopPropagation();
        });
    });
    
    // 拖拽進入效果 / Drag enter effect
    uploadArea.addEventListener('dragover', (e) => {
        e.preventDefault();
        uploadArea.classList.add('dragover');
    });
    
    // 拖拽離開效果 / Drag leave effect
    uploadArea.addEventListener('dragleave', () => {
        uploadArea.classList.remove('dragover');
    });
    
    // 檔案放置處理 / File drop handling
    uploadArea.addEventListener('drop', (e) => {
        e.preventDefault();
        uploadArea.classList.remove('dragover');
        
        const files = e.dataTransfer.files;
        if (files.length > 0) {
            this.handleFileSelect({ target: { files } });
        }
    });
}

/**
 * 設置事件監聽器
 * Setup event listeners
 * 
 * 設置所有必要的事件監聽器
 * Setup all necessary event listeners
 */
setupEventListeners() {
    // 檔案輸入監聽 / File input listener
    const fileInput = document.getElementById('fileInput');
    if (fileInput) {
        fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
    }
    
    // 品質滑桿監聽 / Quality slider listener
    const qualitySlider = document.getElementById('qualitySlider');
    if (qualitySlider) {
        qualitySlider.addEventListener('input', (e) => this.updateQuality(e));
    }
    
    // 轉換按鈕監聽 / Convert button listener
    const convertBtn = document.getElementById('convertBtn');
    if (convertBtn) {
        convertBtn.addEventListener('click', () => this.convertImages());
    }
    
    // 清除按鈕監聽 / Clear button listener
    const clearBtn = document.querySelector('.clear-btn');
    if (clearBtn) {
        clearBtn.addEventListener('click', () => this.clearAll());
    }
}

/**
 * 從後端創建預覽
 * Create preview from backend
 * 
 * 從後端獲取圖片並創建預覽
 * Get image from backend and create preview
 */
async createPreviewFromBackend(fileObj) {
    try {
        // 從後端獲取圖片預覽
        const response = await fetch(`/api/image-converter/download/${fileObj.fileId}`);
        if (response.ok) {
            const blob = await response.blob();
            fileObj.preview = URL.createObjectURL(blob);
        } else {
            // 如果無法獲取預覽,使用佔位符
            fileObj.preview = this.generatePlaceholderImage();
        }
    } catch (error) {
        console.error('創建預覽失敗:', error);
        fileObj.preview = this.generatePlaceholderImage();
    }
}

/**
 * 生成佔位符圖片
 * Generate placeholder image
 * 
 * 為無法載入的圖片生成佔位符
 * Generate placeholder for images that cannot be loaded
 */
generatePlaceholderImage() {
    // 使用 SVG 佔位符圖片
    return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjgwIiBoZWlnaHQ9IjgwIiBmaWxsPSIjRjNGNEY2Ii8+CjxwYXRoIGQ9Ik00MCA0NUMyMi4zIDQ1IDggMzAuNyA4IDEzSDcyQzcyIDMwLjcgNTcuNyA0NSA0MCA0NVoiIGZpbGw9IiNEMUQ1REIiLz4KPC9zdmc+';
}

/**
 * 顯示上傳進度
 * Show upload progress
 * 
 * 顯示檔案上傳進度指示器
 * Display file upload progress indicator
 */
showUploadProgress() {
    const uploadArea = document.getElementById('uploadArea');
    uploadArea.innerHTML = `
        
${this.currentLanguage === 'zh' ? '正在上傳檔案...' : 'Uploading files...'}
${this.currentLanguage === 'zh' ? '請稍候,檔案上傳中' : 'Please wait, uploading files'}
`; uploadArea.style.pointerEvents = 'none'; } /** * 隱藏上傳進度 * Hide upload progress * * 隱藏檔案上傳進度指示器 * Hide file upload progress indicator */ hideUploadProgress() { const uploadArea = document.getElementById('uploadArea'); uploadArea.innerHTML = `
${this.currentLanguage === 'zh' ? '點擊上傳圖片或拖拽到此處' : 'Click to upload images or drag & drop here'}
${this.currentLanguage === 'zh' ? '支援 JPG、PNG、WEBP、BMP 格式,最多 5 張,每張最大 5MB' : 'Supports JPG, PNG, WEBP, BMP formats, up to 5 files, 5MB each'}
`; uploadArea.style.pointerEvents = 'auto'; }
廣告

⚙️ 圖片轉換引擎

功能:核心圖片轉換邏輯,包括格式轉換、品質調整和批次處理

/**
 * 批次轉換處理
 * Batch conversion processing
 * 
 * 批次處理所有選定的圖片
 * Batch process all selected images
 */
async convertAll() {
    if (this.files.length === 0) {
        this.showError(this.getTranslation('noFiles'));
        return;
    }
    
    this.isConverting = true;
    this.showConvertProgress();
    
    try {
        // 準備轉換參數 / Prepare conversion parameters
        const conversions = this.files.map(file => ({
            file_id: file.fileId,
            output_format: file.outputFormat.toLowerCase(),
            quality: this.getCurrentQuality()
        }));
        
        // 呼叫後端 API 進行轉換 / Call backend API for conversion
        const response = await fetch('/api/image-converter/convert', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                conversions: conversions
            })
        });
        
        if (!response.ok) {
            throw new Error(`轉換失敗: ${response.status}`);
        }
        
        const result = await response.json();
        
        // 更新轉換結果 / Update conversion results
        this.convertedFiles = result.converted_files;
        this.updateConvertedFilesList();
        
        // 顯示成功訊息 / Show success message
        this.showSuccess(this.getTranslation('convertSuccess'));
        
    } catch (error) {
        console.error('轉換錯誤:', error);
        this.showError(this.getTranslation('convertError'));
    } finally {
        this.isConverting = false;
        this.hideConvertProgress();
    }
}

/**
 * 單檔轉換
 * Single file conversion
 * 
 * 轉換單一圖片檔案
 * Convert single image file
 */
async convertFile(file) {
    try {
        const response = await fetch('/api/image-converter/convert', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                conversions: [{
                    file_id: file.fileId,
                    output_format: file.outputFormat.toLowerCase(),
                    quality: this.getCurrentQuality()
                }]
            })
        });
        
        if (!response.ok) {
            throw new Error(`轉換失敗: ${response.status}`);
        }
        
        const result = await response.json();
        return result.converted_files[0];
        
    } catch (error) {
        console.error('單檔轉換錯誤:', error);
        throw error;
    }
}

/**
 * 品質調整處理
 * Quality adjustment handling
 * 
 * 處理用戶的品質調整操作
 * Handle user quality adjustment operations
 */
updateQuality(event) {
    const quality = parseInt(event.target.value);
    
    // 更新品質顯示 / Update quality display
    document.getElementById('qualityValue').textContent = quality;
    
    // 即時預覽品質變化 / Real-time quality change preview
    this.debounceQualityPreview(quality);
}

/**
 * 防抖品質預覽
 * Debounced quality preview
 * 
 * 防抖處理品質預覽更新
 * Debounced quality preview updates
 */
debounceQualityPreview(quality) {
    clearTimeout(this.qualityPreviewTimeout);
    this.qualityPreviewTimeout = setTimeout(() => {
        this.updateQualityPreview(quality);
    }, 300);
}

/**
 * 更新品質預覽
 * Update quality preview
 * 
 * 根據品質設定更新預覽效果
 * Update preview based on quality settings
 */
async updateQualityPreview(quality) {
    if (this.files.length === 0) return;
    
    // 取得第一個檔案做為預覽樣本 / Use first file as preview sample
    const sampleFile = this.files[0];
    
    try {
        const response = await fetch('/api/image-converter/preview-quality', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                file_id: sampleFile.fileId,
                quality: quality,
                output_format: sampleFile.outputFormat.toLowerCase()
            })
        });
        
        if (response.ok) {
            const result = await response.json();
            this.updateQualityInfo(result.estimated_size, quality);
        }
    } catch (error) {
        console.error('品質預覽錯誤:', error);
    }
}

/**
 * 獲取當前品質設定
 * Get current quality setting
 */
getCurrentQuality() {
    const qualitySlider = document.getElementById('qualitySlider');
    return qualitySlider ? parseInt(qualitySlider.value) : this.defaultQuality;
}

/**
 * 獲取預設輸出格式
 * Get default output format
 */
getDefaultOutputFormat(inputType) {
    const formatMap = {
        'image/jpeg': 'PNG',
        'image/jpg': 'PNG',
        'image/png': 'JPG',
        'image/webp': 'JPG',
        'image/bmp': 'JPG'
    };
    return formatMap[inputType] || 'JPG';
}

💾 下載與檔案管理

功能:處理檔案下載、批次下載和本地檔案管理

/**
 * 下載單一檔案
 * Download single file
 * 
 * 下載指定的轉換後檔案
 * Download specified converted file
 */
async downloadFile(fileId, fileName) {
    try {
        const response = await fetch(`/api/image-converter/download/${fileId}`);
        
        if (!response.ok) {
            throw new Error(`下載失敗: ${response.status}`);
        }
        
        const blob = await response.blob();
        
        // 建立下載連結 / Create download link
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName;
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
        
    } catch (error) {
        console.error('下載錯誤:', error);
        this.showError(this.getTranslation('downloadError'));
    }
}

/**
 * 批次下載所有檔案
 * Batch download all files
 * 
 * 將所有轉換後的檔案打包下載
 * Package and download all converted files
 */
async downloadAll() {
    if (this.convertedFiles.length === 0) {
        this.showError(this.getTranslation('noConvertedFiles'));
        return;
    }
    
    try {
        // 準備下載清單 / Prepare download list
        const fileIds = this.convertedFiles.map(file => file.file_id);
        
        const response = await fetch('/api/image-converter/download-batch', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                file_ids: fileIds
            })
        });
        
        if (!response.ok) {
            throw new Error(`批次下載失敗: ${response.status}`);
        }
        
        const blob = await response.blob();
        
        // 下載ZIP檔案 / Download ZIP file
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'converted_images.zip';
        document.body.appendChild(a);
        a.click();
        window.URL.revokeObjectURL(url);
        document.body.removeChild(a);
        
    } catch (error) {
        console.error('批次下載錯誤:', error);
        this.showError(this.getTranslation('batchDownloadError'));
    }
}

/**
 * 清理所有檔案
 * Clear all files
 * 
 * 清除所有上傳和轉換的檔案
 * Clear all uploaded and converted files
 */
clearAll() {
    // 清理前端資料 / Clear frontend data
    this.files = [];
    this.convertedFiles = [];
    
    // 清理預覽圖 / Clear preview images
    this.files.forEach(file => {
        if (file.preview) {
            URL.revokeObjectURL(file.preview);
        }
    });
    
    // 重置介面 / Reset interface
    this.updateFileList();
    this.updateConvertedFilesList();
    this.resetQualitySlider();
    
    // 清除檔案輸入 / Clear file input
    const fileInput = document.getElementById('fileInput');
    if (fileInput) {
        fileInput.value = '';
    }
}

/**
 * 移除單一檔案
 * Remove single file
 * 
 * 從清單中移除指定檔案
 * Remove specified file from list
 */
removeFile(fileId) {
    // 尋找並移除檔案 / Find and remove file
    const fileIndex = this.files.findIndex(f => f.id === fileId);
    if (fileIndex > -1) {
        const file = this.files[fileIndex];
        
        // 清理預覽圖 / Clear preview image
        if (file.preview) {
            URL.revokeObjectURL(file.preview);
        }
        
        // 從陣列中移除 / Remove from array
        this.files.splice(fileIndex, 1);
        
        // 更新介面 / Update interface
        this.updateFileList();
    }
}

/**
 * 格式化檔案大小
 * Format file size
 * 
 * 將位元組轉換為易讀格式
 * Convert bytes to readable format
 */
formatFileSize(bytes) {
    if (bytes === 0) return '0 B';
    
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}

/**
 * 更新檔案清單界面
 * Update file list interface
 * 
 * 更新檔案清單的顯示
 * Update file list display
 */
updateFileList() {
    const fileList = document.getElementById('fileList');
    if (!fileList) return;
    
    if (this.files.length === 0) {
        fileList.innerHTML = `
            

尚未上傳任何圖片

`; return; } fileList.innerHTML = this.files.map(file => this.generateFileCard(file)).join(''); }

🎨 使用者介面控制

功能:處理使用者介面更新、狀態顯示和互動回饋

/**
 * 顯示錯誤訊息
 * Show error message
 * 
 * 顯示錯誤提示訊息
 * Display error notification message
 */
showError(message) {
    const errorDiv = document.createElement('div');
    errorDiv.className = 'error-message';
    errorDiv.textContent = message;
    
    // 添加到頁面頂部 / Add to page top
    const container = document.querySelector('.image-converter-container');
    container.insertBefore(errorDiv, container.firstChild);
    
    // 3秒後自動消失 / Auto disappear after 3 seconds
    setTimeout(() => {
        errorDiv.remove();
    }, 3000);
}

/**
 * 顯示成功訊息
 * Show success message
 * 
 * 顯示成功提示訊息
 * Display success notification message
 */
showSuccess(message) {
    const successDiv = document.createElement('div');
    successDiv.className = 'success-message';
    successDiv.textContent = message;
    
    // 添加到頁面頂部 / Add to page top
    const container = document.querySelector('.image-converter-container');
    container.insertBefore(successDiv, container.firstChild);
    
    // 3秒後自動消失 / Auto disappear after 3 seconds
    setTimeout(() => {
        successDiv.remove();
    }, 3000);
}

/**
 * 顯示上傳進度
 * Show upload progress
 * 
 * 顯示檔案上傳進度指示器
 * Display file upload progress indicator
 */
showUploadProgress() {
    const uploadArea = document.getElementById('uploadArea');
    if (uploadArea) {
        uploadArea.classList.add('uploading');
        
        // 更新上傳按鈕文字 / Update upload button text
        const uploadBtn = uploadArea.querySelector('.upload-btn');
        if (uploadBtn) {
            uploadBtn.textContent = this.getTranslation('uploading');
            uploadBtn.disabled = true;
        }
    }
}

/**
 * 隱藏上傳進度
 * Hide upload progress
 * 
 * 隱藏檔案上傳進度指示器
 * Hide file upload progress indicator
 */
hideUploadProgress() {
    const uploadArea = document.getElementById('uploadArea');
    if (uploadArea) {
        uploadArea.classList.remove('uploading');
        
        // 恢復上傳按鈕文字 / Restore upload button text
        const uploadBtn = uploadArea.querySelector('.upload-btn');
        if (uploadBtn) {
            uploadBtn.textContent = this.getTranslation('upload');
            uploadBtn.disabled = false;
        }
    }
}

/**
 * 顯示轉換進度
 * Show conversion progress
 * 
 * 顯示圖片轉換進度
 * Display image conversion progress
 */
showConvertProgress() {
    const convertBtn = document.querySelector('.convert-btn');
    if (convertBtn) {
        convertBtn.textContent = this.getTranslation('converting');
        convertBtn.disabled = true;
        convertBtn.classList.add('loading');
    }
}

/**
 * 隱藏轉換進度
 * Hide conversion progress
 * 
 * 隱藏圖片轉換進度
 * Hide image conversion progress
 */
hideConvertProgress() {
    const convertBtn = document.querySelector('.convert-btn');
    if (convertBtn) {
        convertBtn.textContent = this.getTranslation('convert');
        convertBtn.disabled = false;
        convertBtn.classList.remove('loading');
    }
}

/**
 * 生成檔案卡片HTML
 * Generate file card HTML
 * 
 * 為每個檔案生成顯示卡片
 * Generate display card for each file
 */
generateFileCard(file) {
    return `
        
${file.preview ? `${file.originalName}` : '
無預覽
'}

${file.originalName}

大小: ${this.formatFileSize(file.size)}

`; } /** * 更新檔案格式設定 * Update file format setting * * 更新指定檔案的輸出格式 * Update output format for specified file */ updateFileFormat(fileId, format) { const file = this.files.find(f => f.id === fileId); if (file) { file.outputFormat = format; } }

🚀 工具初始化與啟動

功能:工具的初始化邏輯、事件設定和全域變數建立

/**
 * 設置事件監聽器
 * Setup event listeners
 * 
 * 設置所有必要的事件監聽器
 * Setup all necessary event listeners
 */
setupEventListeners() {
    // 檔案輸入監聽 / File input listener
    const fileInput = document.getElementById('fileInput');
    if (fileInput) {
        fileInput.addEventListener('change', (e) => this.handleFileSelect(e));
    }
    
    // 品質滑桿監聽 / Quality slider listener
    const qualitySlider = document.getElementById('qualitySlider');
    if (qualitySlider) {
        qualitySlider.addEventListener('input', (e) => this.updateQuality(e));
    }
    
    // 轉換按鈕監聽 / Convert button listener
    const convertBtn = document.querySelector('.convert-btn');
    if (convertBtn) {
        convertBtn.addEventListener('click', () => this.convertAll());
    }
    
    // 全部下載按鈕監聽 / Download all button listener
    const downloadAllBtn = document.querySelector('.download-all-btn');
    if (downloadAllBtn) {
        downloadAllBtn.addEventListener('click', () => this.downloadAll());
    }
    
    // 清除按鈕監聽 / Clear button listener
    const clearBtn = document.querySelector('.clear-btn');
    if (clearBtn) {
        clearBtn.addEventListener('click', () => this.clearAll());
    }
}

/**
 * 重置品質滑桿
 * Reset quality slider
 * 
 * 重置品質滑桿到預設值
 * Reset quality slider to default value
 */
resetQualitySlider() {
    const qualitySlider = document.getElementById('qualitySlider');
    const qualityValue = document.getElementById('qualityValue');
    
    if (qualitySlider) {
        qualitySlider.value = this.defaultQuality;
    }
    
    if (qualityValue) {
        qualityValue.textContent = this.defaultQuality;
    }
}

/**
 * 更新品質資訊顯示
 * Update quality info display
 * 
 * 更新品質相關的資訊顯示
 * Update quality related information display
 */
updateQualityInfo(estimatedSize, quality) {
    const qualityInfo = document.getElementById('qualityInfo');
    if (qualityInfo) {
        qualityInfo.innerHTML = `
            
預估大小: ${this.formatFileSize(estimatedSize)}
`; } } // 全域變數建立 / Global variable creation let imageConverter; /** * 文件載入完成後初始化 * Initialize after document loaded * * 確保 DOM 完全載入後再初始化工具 * Ensure tool initialization after DOM is fully loaded */ document.addEventListener('DOMContentLoaded', function() { // 建立圖片轉換器實例 / Create image converter instance imageConverter = new ImageConverter(); // 全域工具引用 / Global tool reference window.imageConverter = imageConverter; console.log('🚀 圖片轉換器已啟動 / Image converter launched'); });

效能最佳化策略

1. 前端效能優化

  • 預覽圖快取:使用 Object URLs 快取預覽圖,減少重複請求
  • 防抖技術:品質調整使用 300ms 防抖,避免頻繁 API 呼叫
  • 分批處理:大量檔案分批上傳和轉換,提升用戶體驗
  • 記憶體管理:即時清理不再需要的 Object URLs

2. 後端效能優化

  • 並行處理:使用 Python 多線程處理批次轉換
  • 記憶體優化:使用 Pillow 的最佳化參數減少記憶體佔用
  • 檔案快取:轉換結果暫時快取,避免重複計算
  • 自動清理:2小時自動清理機制,防止儲存空間溢出

安全性與隱私保護

檔案安全措施

  • 檔案類型驗證:嚴格驗證上傳檔案的 MIME 類型
  • 檔案大小限制:單檔最大 10MB,防止 DoS 攻擊
  • 檔案數量限制:最多 5 個檔案同時處理
  • 路徑安全:使用 UUID 檔案名稱,防止路徑遍歷攻擊

隱私保護機制

  • 本地處理:圖片轉換在伺服器端完成,但不保留副本
  • 自動清理:處理完成後自動清理原始檔案和轉換結果
  • 臨時儲存:所有檔案只在臨時目錄中短期存在
  • 無日誌記錄:不記錄用戶上傳的檔案內容或名稱

瀏覽器相容性

瀏覽器 最低版本 支援功能
Chrome 60+ 完整支援
Firefox 55+ 完整支援
Safari 14+ 完整支援
Edge 79+ 完整支援
Mobile Safari 14+ 基本支援
Chrome Mobile 60+ 完整支援

未來改進方向

🎯 短期目標 (1-3個月)

  • 支援 HEIC/AVIF 格式轉換
  • 增加圖片壓縮選項
  • 實現圖片尺寸調整功能
  • 添加浮水印功能

🚀 中期目標 (3-6個月)

  • 批次處理能力擴展至 20 檔案
  • 實現圖片編輯功能(旋轉、翻轉、裁剪)
  • 添加進階色彩調整選項
  • 支援 RAW 格式轉換

🌟 長期目標 (6個月以上)

  • AI 智能圖片優化
  • 雲端處理擴展
  • API 服務提供
  • 行動應用程式開發

總結

圖片轉換器代表了現代 Web 應用開發的最佳實踐,成功結合了前端的即時互動與後端的高效能處理。 透過混合架構設計,我們實現了既快速又安全的圖片轉換服務。 未來將持續優化效能、擴展功能,為用戶提供更優質的圖片處理體驗。

⚡ 效能表現

平均轉換速度 < 3秒

🔒 安全保障

2小時自動清理

🌐 相容性

支援主流瀏覽器