model-explorer / js /core /modelLoader.js
mr4's picture
Upload 71 files
9bd422a verified
/**
* ModelLoader - Bộ Tải Mô Hình
* Tải danh sách mô hình từ models/models.json và các tệp mô hình ONNX
* Requirements: 1.1, 1.2, 1.3, 1.5, 3.1, 3.2
*/
class ModelLoader {
constructor() {
/** @type {Array|null} In-memory cache for the model list */
this._modelListCache = null;
}
/**
* Tải danh sách mô hình từ tệp cấu hình JSON
* @param {string} configPath - Đường dẫn đến tệp models.json
* @returns {Promise<Array<{id, name, description, path, category, version, size, labelsFile?}>>}
*/
async loadModelList(configPath = CONFIG.MODELS_CONFIG_FILE) {
// Return cached list if available (Requirement 15.4)
if (this._modelListCache !== null) {
console.log('[ModelLoader] Returning cached model list');
return this._modelListCache;
}
try {
const response = await fetch(configPath);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const json = await response.json();
if (!json || !Array.isArray(json.models)) {
throw new Error('Invalid models.json format: missing "models" array');
}
const models = json.models.map(model => ({
id: model.id || '',
name: model.name || '',
description: model.description || '',
path: model.path || '',
category: model.category || '',
version: model.version || '',
size: model.size || 0,
labelsFile: model.labelsFile || null
}));
// Cache the result in memory
this._modelListCache = models;
return models;
} catch (error) {
console.error('[ModelLoader] Failed to load model list:', error);
return [];
}
}
/**
* Tải tệp mô hình ONNX từ đường dẫn
* @param {string} filePath - Đường dẫn đến tệp .onnx
* @returns {Promise<ArrayBuffer>}
* @throws {Error} Nếu không thể tải tệp
*/
async loadModelFile(filePath) {
const response = await fetch(filePath);
if (!response.ok) {
throw new Error(`Failed to load model file "${filePath}": HTTP ${response.status} ${response.statusText}`);
}
const buffer = await response.arrayBuffer();
return buffer;
}
/**
* Xử lý tải lên tệp từ người dùng
* @param {File} file - Đối tượng File từ input hoặc drag-and-drop
* @returns {Promise<{success: boolean, data?: ArrayBuffer, error?: string}>}
*/
async handleFileUpload(file) {
// Xác thực tệp tồn tại
if (!file) {
return { success: false, error: 'No file provided.' };
}
// Xác thực phần mở rộng tệp
const fileName = file.name || '';
const hasValidExtension = CONFIG.FILE.ALLOWED_EXTENSIONS.some(ext =>
fileName.toLowerCase().endsWith(ext)
);
if (!hasValidExtension) {
const allowed = CONFIG.FILE.ALLOWED_EXTENSIONS.join(', ');
return {
success: false,
error: `Invalid file type. Only ${allowed} files are supported.`
};
}
// Xác thực kích thước tệp
if (file.size > CONFIG.FILE.MAX_FILE_SIZE) {
const maxMB = Math.round(CONFIG.FILE.MAX_FILE_SIZE / (1024 * 1024));
return {
success: false,
error: `File is too large. Maximum allowed size is ${maxMB}MB.`
};
}
// Đọc tệp dưới dạng ArrayBuffer
try {
const data = await this._readFileAsArrayBuffer(file);
return { success: true, data };
} catch (error) {
console.error('[ModelLoader] Failed to read uploaded file:', error);
return {
success: false,
error: `Failed to read file: ${error.message || CONFIG.ERRORS.UPLOAD_ERROR}`
};
}
}
/**
* Đọc File object dưới dạng ArrayBuffer
* @param {File} file
* @returns {Promise<ArrayBuffer>}
* @private
*/
_readFileAsArrayBuffer(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => resolve(event.target.result);
reader.onerror = () => reject(new Error(reader.error ? reader.error.message : 'FileReader error'));
reader.readAsArrayBuffer(file);
});
}
}
// Export as global for browser usage
window.ModelLoader = ModelLoader;