Skip to content

Latest commit

 

History

History
556 lines (466 loc) · 19.3 KB

react-native-zip-archive.md

File metadata and controls

556 lines (466 loc) · 19.3 KB

模板版本:v0.2.2

react-native-zip-archive

Supported platforms License

[!TIP] Github 地址

安装与使用

请到三方库的 Releases 发布地址查看配套的版本信息:@react-native-oh-tpl/react-native-zip-archive Releases 。对于未发布到npm的旧版本,请参考安装指南安装tgz包。

进入到工程目录并输入以下命令:

npm

npm install @react-native-oh-tpl/react-native-zip-archive

yarn

yarn add @react-native-oh-tpl/react-native-zip-archive

下面的代码展示了这个库的基本使用场景:

[!WARNING] 使用时 import 的库名不变。

import React, { useState, useEffect } from 'react';
import { View, Button, StyleSheet, TextInput, Alert, Text, ActivityIndicator } from 'react-native';
import { pathParameters, zip, unzip, zipWithPassword, unzipWithPassword, subscribe, creteFile, isPasswordProtected, unzipAssets, getUncompressedSize } from 'react-native-zip-archive';
import { ProgressBar } from 'react-native-paper';

export default function ZipArchiveDemo() {
    const [fileName, setFileName] = useState('');
    const [fileContent, setFileContent] = useState('');
    const [createdFilePath, setCreatedFilePath] = useState('');
    const [compressedFilePath, setCompressedFilePath] = useState('');

    const [newZipPath, setNewZipPath]: any = useState();
    const [newSourcePath, setNewSourcePath]: any = useState();
    const [newFolder, setNewFolder]: any = useState();
    const [password, setPassword] = useState('');
    const [showInput, setShowInput] = useState(false);
    const [zipPassword, setZipPassword] = useState('');
    const [progress, setProgress] = useState(0);
    const [isProgressPing, setIsProgressPing] = useState<boolean>(false);
    const [loading, setLoading] = useState(false);
    const [unzipStatus, setUnzipStatus] = useState('');
    const [uncompressSize, setUncompressSize] = useState('');
    let unzipPassword: string = '';
    let needPassword: boolean = false;

    // 存储设置的密码
    const setUnzipPassword = (value: string) => {
        console.log(`setUnzipPassword: ${value}`);
        unzipPassword = value;
    }

    useEffect(() => {
        let filesDir = pathParameters(); // 获取 HarmonyOS 应用文件路径
        let newZipPath: any = filesDir + '.zip';
        let newSourcePath: any = filesDir;
        let newFolder: any = filesDir + 'Out';//解压时新建个文件夹

        setNewZipPath(newZipPath);//存储压缩包
        setNewSourcePath(newSourcePath);//原文件路径
        setNewFolder(newFolder);//解压时新建个文件夹

        if (!showInput) {
            setPassword(''); // 隐藏输入框时清空密码
        }
    }, [showInput]);

    // 创建文件
    const createFile = () => {
        if (!fileName || !fileContent) {
            Alert.alert('文件名和内容不能为空');
            return;
        }
        const filePath = `${newSourcePath}/${fileName}.txt`;

        if (fileName && fileContent) {
            creteFile(filePath, fileContent)
                .then(() => {
                    setCreatedFilePath(filePath);
                    Alert.alert('文件创建成功')
                    setTimeout(() => {
                        setFileName('');
                        setFileContent('');
                    }, 100);
                })
                .catch((error) => {
                    console.log('文件创建失败:', error);
                });
        } else {
            Alert.alert('请输入文件名和内容');
        }
    };

    // 密码压缩
    const handleZipPress = () => {
        if (password === '') {
            Alert.alert('错误', '请输入密码');
            return;
        }
       
        if (createdFilePath) {
            handleProgress();//进度条
            zipWithPassword(newSourcePath, newZipPath, password)
                .then(() => {
                    console.log(`password--11:${password}`)
                    setZipPassword(password);
                    setCompressedFilePath(newZipPath)
                    Alert.alert('成功', '已使用密码创建压缩');
                })
                .catch(error => {
                    Alert.alert('错误', `创建压缩文件失败: ${error}`);
                });
        } else {
            Alert.alert('无文件可供压缩');
        }
    };

    // 解压时是否需要密码
    const isUnzipWithPassword = () => {
        if (unzipPassword) {
            if (zipPassword === '') {
                Alert.alert('错误', '请先进行压缩并设置密码');
                return;
            }

            if (unzipPassword === zipPassword) {
                handleGetUncompressedSize();
                handleProgress();//进度条
                unzipWithPassword(newZipPath,newFolder, unzipPassword)
                    .then(() => {
                        Alert.alert('成功', '已使用密码解压文件');
                    })
                    .catch(error => {
                        Alert.alert('错误', `解压文件失败: ${error}`);
                    });

            } else {
                Alert.alert('密码输入错误');
            }
        } else {
            Alert.alert('错误', '请先输入密码');
        }
    }

    // 密码解压&解压
    const handleUnzipPress = () => {
        if (this.needPassword) {
            isUnzipWithPassword();
            setShowInput(true);
            return;
        }
        isPasswordProtected(newZipPath)
            .then((res) => {
                if (res) {
                    this.needPassword = true;
                    setShowInput(true);
                } else {
                    if (compressedFilePath) {
                        if (needPassword === false) {
                            handleGetUncompressedSize();
                            handleProgress();//进度条
                            unzip(newZipPath, newFolder,'UTF-8')
                                .then(() => {
                                    console.log(`unzip success`)
                                    Alert.alert('成功', '已解压');
                                })
                                .catch(error => {
                                    Alert.alert('错误', '解压失败');
                                    console.log(`unzip error: ${error}`);
                                })
                        }
                    } else {
                        Alert.alert('无压缩文件可供解压');
                    }
                }
            })
            .catch(error => {
                console.error(`isPasswordProtected error: ${error}`)
            })
    }

    // 进度条
    const handleProgress = () => {
        setIsProgressPing(true);
        setProgress(0); //重置进度条
        interface data {
            progress: number,
            filePath: string
        }
        let currentProgress = 0;

        const interval = setInterval(() => {
            if (currentProgress < 100) {
                currentProgress += 20;
                setProgress(currentProgress);
                console.log(`current progress: ${currentProgress}%`);
            } else {
                clearInterval(interval); // 达到最大值后清除 interval
                setIsProgressPing(false);
            }
        }, 1000);

        subscribe((data: data) => {
            try {
                if (data.progress != null) {
                    currentProgress = data.progress;
                    setProgress(data.progress);
                    console.log(`subscribe success: ${data.progress}`);
                    setIsProgressPing(false);

                    if (data.progress === 100) {
                        clearInterval(interval);
                    }
                }
            } catch (error) {
                console.log(`subscribe error: ${error}`);
                clearInterval(interval);
            }
        })
    }

    // unzipAssets
    const handleUnzipAssets = async () => {
        setLoading(true);
        let filesDir = pathParameters();
        //解压files.zip文件到系统中的 destinationFolder 目录中
        let assetPath = filesDir + '.zip';
        let targetPath = filesDir + 'destinationFolder';
        if (compressedFilePath) {
            try {
                await unzipAssets(assetPath, targetPath);
                setUnzipStatus('解压完成');
                console.log(`unzipAssets success`);
            } catch (err) {
                setUnzipStatus(`解压失败:${err}`);
                console.log(`unzipAssets err: ${err}`);
            } finally {
                setLoading(false);
            }
        } else {
            Alert.alert('无压缩文件可供解压');
        }
    }

    // getUncompressedSize
    const handleGetUncompressedSize = () => {
        getUncompressedSize(newZipPath)
            .then((uncompressSize:any) => {
                setUncompressSize(uncompressSize);
                console.log(`uncompressSize success:${uncompressSize}`)
            })
            .catch((err) => {
                console.log(`getUncompressedSize err:${err}`)
            })
    }

    return (
        <View style={styles.content}>
            <View style={styles.buttonSix}>
                <View>
                    <Text>解压缩后的大小:{uncompressSize ? uncompressSize : '0'}字节</Text>
                    <View style={styles.progressBar}>
                        <View style={{ width: `${progress}%`, backgroundColor: '#00AEEF', height: '100%' }}></View>
                    </View>
                    <Text style={styles.percentageText}>{progress}%</Text>
                </View>
            </View>

            <View >
                <View >
                    <TextInput
                        placeholder="请输入文件名"
                        value={fileName}
                        onChangeText={setFileName}
                        style={{ borderWidth: 1, padding: 10, width: '70%' }}
                    />
                    <TextInput
                        style={{
                            height: 100,
                            borderColor: 'gray',
                            borderWidth: 1,
                            width: 200,
                            padding: 10,
                            marginBottom: 10,
                            marginTop: 10
                        }}
                        onChangeText={text => setFileContent(text)}
                        value={fileContent}
                        placeholder="文件内容"
                        multiline={true}
                    />
                    <Button title="创建文件" onPress={createFile} />
                </View>
            </View>

            <View style={styles.buttonSix}>
                <Button title='压缩' disabled={!createdFilePath} onPress={() => {
                    if (createdFilePath) {
                        handleProgress();//进度条
                        zip(newSourcePath, newZipPath)
                            .then(() => {
                                setCompressedFilePath(newZipPath)
                                Alert.alert('成功', '已压缩');
                            })
                            .catch(error => {
                                Alert.alert('错误', `压缩失败: ${error}`);
                            })
                    } else {
                        Alert.alert('无文件可供压缩');
                    }
                }} />
            </View>

            <View>
                <TextInput
                    style={styles.input}
                    placeholder="设置压缩密码"
                    onChangeText={text => setPassword(text)}
                    value={password}
                />
            </View>
            <View style={styles.buttonSix}>
                <Button title='密码压缩' disabled={!createdFilePath} onPress={handleZipPress} />
            </View>

            {showInput && (
                <View>
                    <TextInput
                        style={styles.input}
                        placeholder="输入解压密码"
                        onChangeText={text => setUnzipPassword(text)}
                    />
                </View>
            )}

            <View style={styles.buttonSix}>
                <Button title="解压" disabled={!createdFilePath} onPress={handleUnzipPress} />
            </View>
        </View>
    )
}

const styles = StyleSheet.create({
    content: {
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        marginTop: 56
    },
    buttonSix: {
        width: '65%',
        marginBottom: 10,
        marginTop: 20
    },
    input: {
        borderWidth: 1,
        padding: 10,
        width: 300
    },
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    progressBar: {
        width: 250,
        height: 20,
        backgroundColor: '#E0E0E0',
        borderRadius: 10,
        overflow: 'hidden',
        marginTop: 10
    },
    percentageText: {
        marginTop: 5,
        fontSize: 16
    }
})

使用 Codegen

本库已经适配了 Codegen ,在使用前需要主动执行生成三方库桥接代码,详细请参考 Codegen 使用文档

Link

目前 HarmonyOS 暂不支持 AutoLink,所以 Link 步骤需要手动配置。

首先需要使用 DevEco Studio 打开项目里的 HarmonyOS 工程 harmony

1.在工程根目录的 oh-package.json5 添加 overrides 字段

{
  ...
  "overrides": {
    "@rnoh/react-native-openharmony" : "./react_native_openharmony"
  }
}

2.引入原生端代码

目前有两种方法:

  1. 通过 har 包引入(在 IDE 完善相关功能后该方法会被遗弃,目前首选此方法);
  2. 直接链接源码。

方法一:通过 har 包引入(推荐)

[!TIP] har 包位于三方库安装路径的 harmony 文件夹下。

打开 entry/oh-package.json5,添加以下依赖

"dependencies": {
    "@rnoh/react-native-openharmony": "file:../react_native_openharmony",
+   "@react-native-oh-tpl/react-native-zip-archive": "file:../../node_modules/@react-native-oh-tpl/react-native-zip-archive/harmony/zipArchive_package.har"
  }

点击右上角的 sync 按钮

或者在终端执行:

cd entry
ohpm install

方法二:直接链接源码

[!TIP] 如需使用直接链接源码,请参考直接链接源码说明

3.配置 CMakeLists 和引入zipArchive

打开 entry/src/main/cpp/CMakeLists.txt,添加:

project(rnapp)
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_SKIP_BUILD_RPATH TRUE)
set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
+ set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../react-native-harmony/harmony/cpp")
set(LOG_VERBOSITY_LEVEL 1)
set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
add_compile_definitions(WITH_HITRACE_SYSTRACE)

add_subdirectory("${RNOH_CPP_DIR}" ./rn)

# RNOH_BEGIN: manual_package_linking_1
add_subdirectory("../../../../sample_package/src/main/cpp" ./sample-package)
+ add_subdirectory("${OH_MODULES}/@react-native-oh-tpl/react-native-zip-archive/src/main/cpp" ./zipArchive-package)
# RNOH_END: manual_package_linking_1

file(GLOB GENERATED_CPP_FILES "./generated/*.cpp")

add_library(rnoh_app SHARED
    ${GENERATED_CPP_FILES}
    "./PackageProvider.cpp"
    "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
)
target_link_libraries(rnoh_app PUBLIC rnoh)

# RNOH_BEGIN: manual_package_linking_2
target_link_libraries(rnoh_app PUBLIC rnoh_sample_package)
+ target_link_libraries(rnoh_app PUBLIC nativi_minizip)
# RNOH_END: manual_package_linking_2

4.在 ArkTs 侧引入 ZipArchivePackage

打开 entry/src/main/ets/RNPackagesFactory.ts,添加:

+ import {ZipArchivePackage} from '@react-native-oh-tpl/react-native-zip-archive/ts';

export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
  return [
    new SamplePackage(ctx),
+   new ZipArchivePackage(ctx)
  ];
}

5.运行

点击右上角的 sync 按钮

或者在终端执行:

cd entry
ohpm install

然后编译、运行即可。

约束与限制

兼容性

要使用此库,需要使用正确的 React-Native 和 RNOH 版本。另外,还需要使用配套的 DevEco Studio 和 手机 ROM。

请到三方库相应的 Releases 发布地址查看 Release 配套的版本信息:@react-native-oh-library/react-native-zip-archive Releases

API

[!TIP] "Platform"列表示该属性在原三方库上支持的平台。

[!TIP] "HarmonyOS Support"列为 yes 表示 HarmonyOS 平台支持该属性;no 则表示不支持;partially 表示部分支持。使用方法跨平台一致,效果对标 iOS 或 Android 的效果。

Name Description Type Required Platform HarmonyOS Support
zip File or folder compression string no All yes
unzip Decompression of files or folders string no All yes
zipWithPassword Compress files or folders with a password string no All yes
unzipWithPassword Extract files or folders with a password string no All yes
isPasswordProtected Check if the password exists during password decompression boolean no All yes
unzipAssets Pass in the specified directory during decompression string no All yes
getUncompressedSize File size after decompression number no All yes
subscribe Display progress bar during compression and password decompression void no All yes

遗留问题

其他

开源协议

本项目基于 The MIT License (MIT) ,请自由地享受和参与开源。