Android NDK系列- 文件拆分与合并

Posted SingleShu888

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android NDK系列- 文件拆分与合并相关的知识,希望对你有一定的参考价值。

NDK系列(一)-AS使用javah生成so文件
NDK系列(二)-AS使用CmakeLists生成so文件
NDK系列(三)-AS编写C文件没有提示和不识别NULL
NDK系列(四)-AS生成jar包、导入so库并使用方法
NDK系列(五)- AS导入so三方库,使用C/C+方法

文件的拆分和合并是一个常见的功能,使用java是可以完成的。
Android合并文件的三种方式 ,通过FileOutputStream、与FileInputStream方式,通过FileChannel方式,通过RandomAccessFile方式。还可以使用C来拆分和合并文件,这是一个效率很高的方式。至于java和C需要哪种来实现完全看自己的需求。下面参考一篇C的文件拆分合并代码。C语言学习笔记之文件的分割与合并


第一步:理清文件拆分和合并的逻辑
1,首先需要拆分文件的地址,生成拆分后子文件的地址,子文件(可以使用原文件名_%d命名,然后在代码中进行替换,如果嫌麻烦也可以直接用传入多个地址是一样的)。
2,需要知道拆分的子文件个数
3,源文件的大小,以及是否能整除子文件个数(这里分两种情况处理)
4,读取源文件,循环写入子文件。(这里拆分需要一个个char读取和写入,不然会出问题)
5,关闭文件流,释放资源
6,合并逻辑和拆分逻辑类似,只是读取子文件数据,写入源文件。


第二步:代码阶段

首先创建一个C++的 android项目(这个在之前有介绍),这个很简单,删掉自动生成的jni方法。

需要申明两个native方法,一个拆分文件,一个合并文件,传入的参数都是一样的。在这个项目中不需要修改cmakelists文件,没有导入其他so文件,当然你要想把这个生成so自己留着用,也是可以的。那么具体逻辑在cpp文件中实现。


调试的时候需要打印数据,那么申明一个打印方法。先做好准备工作,首先需要一个方法来获取一个文件的大小值。

long get_file_size(const char* path) 
    FILE *fp = fopen(path, "rb"); //打开一个文件, 文件必须存在,只运行读
    fseek(fp, 0, SEEK_END);
    long ret = ftell(fp);
    fclose(fp);
    return ret;

这是C里面打开一个文件,读取一个文件大小。

JNIEXPORT void JNICALL
Java_com_example_administrator_jnidiffdemo_MainActivity_splitFile ( JNIEnv * env ,
                                                                    jobject instance ,
                                                                    jstring srcFilePath_ ,
                                                                    jstring dstFilePath_ ,
                                                                    jint file_num ) 
    //    1,首先需要拆分文件的地址,生成拆分后子文件的地址,子文件(可以使用原文件名_%d命名,然后在代码中进行替换,如果嫌麻烦也可以直接用传入多个地址是一样的)。
//2,需要知道拆分的子文件个数
    const char * srcFilePath = env -> GetStringUTFChars ( srcFilePath_ , NULL );
    const char * dstFilePath = env -> GetStringUTFChars ( dstFilePath_ , NULL );

    LOGI( "JNI native diff begin" );
    //申请存放多个文件的内存  二级指针首地址
    char ** patches = ( char ** ) malloc ( sizeof ( char * ) * file_num );

    for ( int i = 0 ; i < file_num ; i ++ ) 
        patches[ i ] = ( char * ) malloc ( sizeof ( char ) * 100 );
        //对每个地址进行拼接
        sprintf ( patches[ i ] , dstFilePath , i );
        LOGI( "patch path : %s" , patches[ i ] );
    

//  3,源文件的大小,以及是否能整除子文件个数(这里分两种情况处理) 读取源文件大小  根据大小进行拆分
    int fileSize = get_file_size ( srcFilePath );
    LOGI( "fileSize : %d" , fileSize );
//    打开源文件
    FILE * rfp = fopen ( srcFilePath , "rb" );
//   4,读取源文件,循环写入子文件。(这里拆分需要一个个char读取和写入,不然会出问题) 源文件是否正好整除所划分的文件个数  分别处理
    if ( fileSize % file_num == 0 ) 
        int part = fileSize / file_num;
        for ( int i = 0 ; i < file_num ; i ++ ) 
            FILE * fwp = fopen ( patches[ i ] , "wb" );
            for ( int j = 0 ; j < part ; j ++ ) 
                fputc ( fgetc ( rfp ) , fwp );
            
            fclose ( fwp );
        
     else 
        int part = fileSize / ( file_num - 1 );
        for ( int i = 0 ; i < file_num - 1 ; i ++ ) 
            FILE * fwp = fopen ( patches[ i ] , "wb" );
            for ( int j = 0 ; j < part ; j ++ ) 
                fputc ( fgetc ( rfp ) , fwp );
            
            fclose ( fwp );
        
        FILE * fwp = fopen ( patches[ file_num - 1 ] , "wb" );
        for ( int i = 0 ; i < fileSize % ( file_num - 1 ) ; i ++ ) 
            fputc ( fgetc ( rfp ) , fwp );
        
        fclose ( fwp );
    
//  5,关闭文件流,释放资源
    fclose ( rfp );
    for ( int i = 0 ; i < file_num ; i ++ ) 
        free ( patches[ i ] );
    
    free ( patches );

    env -> ReleaseStringUTFChars ( srcFilePath_ , srcFilePath );
    env -> ReleaseStringUTFChars ( dstFilePath_ , dstFilePath );

这里的逻辑都有注释,按照之前写的5步逻辑,那么合并就相对于简单了

JNIEXPORT void JNICALL
Java_com_example_administrator_jnidiffdemo_MainActivity_mergeFile ( JNIEnv * env ,
                                                                    jobject instance ,
                                                                    jstring srcFilePath_ ,
                                                                    jstring dstFilePath_ ,
                                                                    jint fileNum ) 
    const char * srcFilePath = env -> GetStringUTFChars ( srcFilePath_ , 0 );
    const char * dstFilePath = env -> GetStringUTFChars ( dstFilePath_ , 0 );

    char** patches = ( char**)malloc ( sizeof ( char*)*fileNum);
    for ( int i = 0 ; i < fileNum ;  i++ ) 
        patches[i] = (char*)malloc ( sizeof ( char)*100);
        sprintf (patches[i],dstFilePath,i);
        LOGI("patches[i]:%s",patches[i]);
    

    FILE *fwp = fopen (srcFilePath,"wb");

    for ( int i = 0 ; i < fileNum ; i++ ) 
        int fileSize = get_file_size (patches[i]);
        FILE *frp = fopen (patches[i],"rb");
        LOGI("fileSize:%d",fileSize);
        for ( int j = 0 ; j < fileSize ; j++ ) 
            fputc (fgetc (frp),fwp);
        
        fclose (frp);
    
    fclose (fwp);

    for ( int i = 0 ; i < fileNum ;  i++ ) 
        free (patches[i]);
    
    free (patches);

    env -> ReleaseStringUTFChars ( srcFilePath_ , srcFilePath );
    env -> ReleaseStringUTFChars ( dstFilePath_ , dstFilePath );

在MainActivity调用。

 public void split(View View)
        String srcFile = SD_DIR+"/"+"a.mp4";
        String dstFile = SD_DIR+"/"+"a_%d.mp4";
        splitFile(srcFile,dstFile,4);
    

    public void merge(View view)
        String srcFile = SD_DIR+"/"+"a.mp4";
        String dstFile = SD_DIR+"/"+"a_%d.mp4";
        mergeFile(srcFile,dstFile,4);
    

基本就这些了,只要踏入了门槛,其他的都是C的知识多一点,不会的也可以在网上查。
github地址:https://github.com/SingleShu/JniDiffDemo

感谢动脑学院NDK专题资源

以上是关于Android NDK系列- 文件拆分与合并的主要内容,如果未能解决你的问题,请参考以下文章

Android NDK系列- AS导入so三方库,使用C/C+方法

Android NDK系列- AS导入so三方库,使用C/C+方法

Android NDK系列-AS使用javah生成so文件

Android NDK系列-AS生成jar包导入so库并使用方法

Android NDK系列-AS生成jar包导入so库并使用方法

Android NDK系列-AS使用CmakeLists生成so文件