欢迎大家加入开源鸿蒙跨平台开发者社区,一起共建开源鸿蒙跨平台生态。

在这里插入图片描述

📌 模块概述

导入导出模块是MovieTracker应用中用于数据交换的功能。用户可以将应用数据导出为JSON或CSV格式,也可以从这些格式的文件导入数据。导入导出功能支持与其他应用或设备的数据交换,提高了数据的可移植性。

该模块的主要功能包括:导出为JSON、导出为CSV、导入JSON、导入CSV、数据验证等。通过Cordova框架与OpenHarmony原生能力的结合,实现了完整的数据导入导出功能。

导入导出需要处理不同格式的数据转换,同时需要验证导入数据的有效性。

🔗 完整流程

第一步:数据导出

用户可以选择导出格式(JSON或CSV)和导出范围(所有数据或特定数据)。导出过程包括数据序列化、格式转换、文件生成等步骤。

导出完成后文件保存到用户指定的位置,用户可以通过文件管理器访问导出的文件。

第二步:数据导入

用户可以选择要导入的文件。导入过程包括文件读取、格式识别、数据验证、数据导入等步骤。

导入前需要验证数据的有效性,确保导入的数据不会破坏现有数据。导入时可以选择覆盖或合并现有数据。

第三步:数据验证与处理

导入数据前需要进行验证,检查数据的完整性和有效性。验证失败时需要显示详细的错误信息,告知用户具体的问题。

同时需要处理数据冲突,如重复的影片等。

🔧 Web代码实现

导入导出HTML结构

<div id="import-export-page" class="page">
    <div class="page-header">
        <h2>导入导出</h2>
    </div>
    
    <div class="import-export-container">
        <div class="export-section">
            <h3>导出数据</h3>
            <div class="form-group">
                <label>导出格式:</label>
                <select id="export-format" class="form-select">
                    <option value="json">JSON 格式</option>
                    <option value="csv">CSV 格式</option>
                </select>
            </div>
            <button class="btn btn-primary" onclick="exportData()">📥 导出</button>
        </div>
        
        <div class="import-section">
            <h3>导入数据</h3>
            <input type="file" id="import-file" accept=".json,.csv" class="form-input">
            <div class="form-group">
                <label>导入方式:</label>
                <select id="import-mode" class="form-select">
                    <option value="merge">合并</option>
                    <option value="replace">覆盖</option>
                </select>
            </div>
            <button class="btn btn-primary" onclick="importData()">📤 导入</button>
        </div>
    </div>
</div>

导入导出实现

async function exportData() {
    try {
        const format = document.getElementById('export-format').value;
        const allMovies = await db.getAllMovies();
        
        let content, filename;
        
        if (format === 'json') {
            content = JSON.stringify(allMovies, null, 2);
            filename = `movies_${Date.now()}.json`;
        } else {
            content = convertToCSV(allMovies);
            filename = `movies_${Date.now()}.csv`;
        }
        
        downloadFile(content, filename);
        showSuccess('数据已导出');
    } catch (error) {
        console.error('导出失败:', error);
        showError('导出失败');
    }
}

function convertToCSV(movies) {
    const headers = ['标题', '导演', '年份', '评分', '分类', '状态'];
    const rows = movies.map(m => [
        m.title,
        m.director,
        m.year,
        m.rating || '',
        m.category || '',
        m.status
    ]);
    
    const csv = [headers, ...rows]
        .map(row => row.map(cell => `"${cell}"`).join(','))
        .join('\n');
    
    return csv;
}

function downloadFile(content, filename) {
    const blob = new Blob([content], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.click();
    URL.revokeObjectURL(url);
}

async function importData() {
    const fileInput = document.getElementById('import-file');
    const file = fileInput.files[0];
    
    if (!file) {
        showError('请选择文件');
        return;
    }
    
    try {
        const content = await readFile(file);
        const mode = document.getElementById('import-mode').value;
        
        let data;
        if (file.name.endsWith('.json')) {
            data = JSON.parse(content);
        } else if (file.name.endsWith('.csv')) {
            data = parseCSV(content);
        } else {
            showError('不支持的文件格式');
            return;
        }
        
        // 验证数据
        const errors = validateImportData(data);
        if (errors.length > 0) {
            showError('数据验证失败:\n' + errors.join('\n'));
            return;
        }
        
        // 导入数据
        if (mode === 'replace') {
            await db.clearAllMovies();
        }
        
        for (let movie of data) {
            await db.addMovie(movie);
        }
        
        showSuccess('数据已导入');
        fileInput.value = '';
    } catch (error) {
        console.error('导入失败:', error);
        showError('导入失败: ' + error.message);
    }
}

function readFile(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = e => resolve(e.target.result);
        reader.onerror = reject;
        reader.readAsText(file);
    });
}

function parseCSV(content) {
    const lines = content.split('\n');
    const headers = lines[0].split(',').map(h => h.replace(/"/g, ''));
    const data = [];
    
    for (let i = 1; i < lines.length; i++) {
        if (!lines[i].trim()) continue;
        
        const values = lines[i].split(',').map(v => v.replace(/"/g, ''));
        const movie = {
            title: values[0],
            director: values[1],
            year: parseInt(values[2]),
            rating: values[3] ? parseFloat(values[3]) : null,
            category: values[4],
            status: values[5]
        };
        data.push(movie);
    }
    
    return data;
}

function validateImportData(data) {
    const errors = [];
    
    if (!Array.isArray(data)) {
        errors.push('数据格式不正确');
        return errors;
    }
    
    data.forEach((movie, index) => {
        if (!movie.title) errors.push(`${index + 1} 行: 标题不能为空`);
        if (!movie.director) errors.push(`${index + 1} 行: 导演不能为空`);
        if (!movie.year) errors.push(`${index + 1} 行: 年份不能为空`);
    });
    
    return errors;
}

🔌 OpenHarmony原生代码

导入导出插件

// ImportExportPlugin.ets
import { webview } from '@kit.ArkWeb';
import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';

export class ImportExportPlugin {
    private context: common.UIAbilityContext;
    
    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }
    
    public registerImportExport(controller: webview.WebviewController): void {
        controller.registerJavaScriptProxy({
            object: new ImportExportBridge(this.context),
            name: 'importExportNative',
            methodList: ['exportToFile', 'importFromFile']
        });
    }
}

导入导出实现

export class ImportExportBridge {
    private context: common.UIAbilityContext;
    
    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }
    
    public exportToFile(dataJson: string, format: string): string {
        try {
            const timestamp = Date.now();
            const filename = `movies_${timestamp}.${format}`;
            const filepath = `${this.context.filesDir}/${filename}`;
            
            const file = fileIo.openSync(filepath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE);
            fileIo.writeSync(file.fd, dataJson);
            fileIo.closeSync(file.fd);
            
            return JSON.stringify({
                success: true,
                filepath: filepath,
                filename: filename
            });
        } catch (error) {
            return JSON.stringify({
                success: false,
                error: error.message
            });
        }
    }
    
    public importFromFile(filepath: string): string {
        try {
            const content = fileIo.readTextSync(filepath);
            const data = JSON.parse(content);
            
            return JSON.stringify({
                success: true,
                data: data,
                count: Array.isArray(data) ? data.length : 1
            });
        } catch (error) {
            return JSON.stringify({
                success: false,
                error: error.message
            });
        }
    }
}

📝 总结

导入导出模块展示了Cordova与OpenHarmony混合开发中的数据交换功能。通过Web层提供导入导出界面,同时利用OpenHarmony原生能力进行文件操作。

在实现这个模块时,需要注意数据格式的转换、数据验证的完整性、以及用户体验的流畅性。通过合理的架构设计,可以构建出高效、易用的导入导出功能。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐