美文网首页
NDK--文件的拆分和合并

NDK--文件的拆分和合并

作者: aruba | 来源:发表于2020-04-17 09:10 被阅读0次

    断点续传中,我们需要将一个文件拆分多个文件,并通过多线程上传,今天利用JNI实现文件的拆分和合并,调用c/c++的方式,性能会有所提升。

    1.创建文件工具类

    这边将文件封装成c++类,减少了繁琐的操作

    _FileClass.h

    //
    // Created by aruba on 2020/4/16.
    //
    
    #ifndef FILECLASS_H
    #define FILECLASS_H
    
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdarg.h>
    
    class CFILE {
    
    private:
        FILE *m_fp;
        char m_filename[301];
        bool m_buffenable;
    
    public:
        //构造函数
        CFILE();
    
        //构造函数,带参数
        CFILE(bool buffenable);
    
        //打开文件
        bool Open(const char *filename, const char *mode);
    
        //fgets,读文件
        bool Fgets(char *buff, int readsize);
    
        //fgets,一个字节读文件
        int Fgetc();
        
        //写文件
        void Fprintf(const char *write);
    
        //写文件
        void Fprintf(const char *format, ...);
    
        //一个字节写文件
        void Fputc(const int c);
        
        //获取文件大小
        long GetFileSize();
        
        //析构函数
        ~CFILE();
    };
    
    
    #endif //FILECLASS_H
    
    

    _FileClass.cpp

    //
    // Created by aruba on 2020/4/16.
    //
    
    #include "_FileClass.h"
    
    //构造函数初始化
    CFILE::CFILE() {
        m_fp = 0;
        memset(m_filename, 0, sizeof(m_filename));
        m_buffenable = true;
    }
    
    //带参数构造函数初始化
    CFILE::CFILE(bool buffenable) {
        m_fp = 0;
        memset(m_filename, 0, sizeof(m_filename));
        m_buffenable = buffenable;
    }
    
    //打开文件
    bool CFILE::Open(const char *filename, const char *mode) {
        if ((m_fp = fopen(filename, mode)) == 0) return false;
    
        strcpy(m_filename, filename);
    
        return true;
    }
    
    //fgets,读文件
    bool CFILE::Fgets(char *buff, int readsize) {
        if (m_fp == 0) return false;
    
        if (access(m_filename, R_OK) == -1) return false;
    
        //初始化buff
        memset(buff, 0, sizeof(readsize));
    
        if (fgets(buff, readsize, m_fp) == 0) return false;
    
        return true;
    }
    
    //fgets,一个字节读文件
    int CFILE::Fgetc() {
        if (m_fp == 0) return EOF;
    
        return fgetc(m_fp);
    }
    
    //写文件
    void CFILE::Fprintf(const char *write) {
        if (m_fp == 0) return;
    
        if (access(m_filename, W_OK) == -1) return;
    
        fprintf(m_fp, write);
    
        if (m_buffenable == false) fflush(m_fp);
    }
    
    //写文件
    void CFILE::Fprintf(const char *format, ...) {
        if (m_fp == 0) return;
    
        if (access(m_filename, W_OK) == -1) return;
    
        va_list va;
        va_start(va, format);
    
        fprintf(m_fp, format);
    
        va_end(va);
    
        if (m_buffenable == false) fflush(m_fp);
    }
    
    //一个字节写文件
    void CFILE::Fputc(const int c) {
        if (m_fp == 0) return;
    
        fputc(c, m_fp);
        if (m_buffenable == false) fflush(m_fp);
    }
    
    //获取文件大小
    long CFILE::GetFileSize() {
        if (m_fp == 0) return 0;
    
        if (access(m_filename, R_OK) == -1) return 0;
    
        //将文件指针移动到文件尾
        fseek(m_fp, 0, SEEK_END);
        long size = ftell(m_fp);
    
        //还原文件指针
        rewind(m_fp);
        return size;
    }
    
    //析构函数
    CFILE::~CFILE() {
        fclose(m_fp);
        m_fp = 0;
        m_buffenable = true;
        memset(m_filename, 0, sizeof(m_filename));
    }
    
    注意:记得在CMakeList中添加

    2.创建Java工具类,定义文件拆分和合并的native方法

    package com.aruba.ndkapplication;
    
    /**
     * 文件拆分和合并
     */
    public class FileDiffUtils {
        //拆分文件
        public static native void diff(String filePath, String partPath, int num);
    
        //合并文件
        public static native void merge(String mergeFilePath, String partPath, int num);
    }
    
    

    3.以动态注册的方式连接native方法

    //文件拆分和合并
    static const JNINativeMethod gMethodsFileDiff[] = {
            {
                    "diff",  "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_diff
            },
            {
                    "merge", "(Ljava/lang/String;Ljava/lang/String;I)V", (void *) native_merge
            }
    };
    
    //注册文件拆分和合并
    static int registerNativesFileDiff(JNIEnv *env) {
        LOGI("registerNatives begin");
        jclass clazz;
        //找到java的类
        clazz = env->FindClass("com/aruba/ndkapplication/FileDiffUtils");
    
        if (clazz == NULL) {
            LOGI("clazz is null");
            return JNI_FALSE;
        }
    
        if (env->RegisterNatives(clazz, gMethodsFileDiff, NELEM(gMethodsFileDiff)) < 0) {
            LOGI("RegisterNatives error");
            return JNI_FALSE;
        }
    
        return JNI_TRUE;
    }
    
    JNI_OnLoad方法中调用
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
        LOGI("jni_OnLoad begin");
    
        JNIEnv *env = NULL;
    
        if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
            LOGI("ERROR: GetEnv failed\n");
            return -1;
        }
    
        assert(env != NULL);
    
        registerNatives(env);
        registerNativesFileDiff(env);
    
        return JNI_VERSION_1_4;
    }
    

    4.实现文件拆分和合并方法

    //二进制一个个字节写文件
    void putPartFilec(JNIEnv *env, const long size, const char *partPath, CFILE *readFile) {
        CFILE partFile;
        //打开拆分文件
        if (!partFile.Open(partPath, "w")) {
            jclass newExc = env->FindClass("java/lang/Exception");
            char info[500];
            memset(info, 0, 500);
            sprintf(info, "can not open :%s", partPath);
            if (newExc != NULL) env->ThrowNew(newExc, info);
            env->DeleteLocalRef(newExc);
    
            return;
        }
    
        for (int j = 0; j < size; j++) {
            int c = readFile->Fgetc();
            partFile.Fputc(c);
        }
    }
    
    //拆分文件
    JNIEXPORT void JNICALL
    native_diff(JNIEnv *env, jclass type, jstring filePath, jstring partPath, jint num) {
        //读取的文件
        const char *readPath = env->GetStringUTFChars(filePath, NULL);
        //拆分文件
        const char *wirtePath = env->GetStringUTFChars(partPath, NULL);
    
        //读文件
        CFILE readFile;
        if (!readFile.Open(readPath, "r")) {
            jclass newExc = env->FindClass("java/lang/Exception");
            char info[500];
            memset(info, 0, 500);
            sprintf(info, "can not open :%s", filePath);
            if (newExc != NULL) env->ThrowNew(newExc, info);
            env->DeleteLocalRef(newExc);
    
            env->ReleaseStringUTFChars(partPath, wirtePath);
            env->ReleaseStringUTFChars(filePath, readPath);
            return;
        }
    
        //生成拆分文件绝对路径数组
        char partPaths[num][500];
        memset(partPaths, 0, sizeof(partPaths));
        for (int i = 0; i < num; i++) {
            sprintf(partPaths[i], wirtePath, i);
        }
    
        //计算每个文件大小
        long fileSize = readFile.GetFileSize();
        //整除的情况,均分
        if (fileSize % num == 0) {
            //每个拆分文件的大小
            long partFileSize = fileSize / num;
    
            //循环写入文件
            for (int i = 0; i < num; i++) {
                putPartFilec(env, partFileSize, partPaths[i], &readFile);
            }
        } else {
            //前(num - 1)个拆分文件的大小
            long partFileSize = fileSize / num ;
            //循环写入文件
            for (int i = 0; i < num - 1; i++) {
                putPartFilec(env, partFileSize, partPaths[i], &readFile);
            }
    
            //第num个拆分文件的大小
            partFileSize = partFileSize + fileSize % num;
            putPartFilec(env, partFileSize, partPaths[num - 1], &readFile);
        }
    
        env->ReleaseStringUTFChars(partPath, wirtePath);
        env->ReleaseStringUTFChars(filePath, readPath);
    }
    
    //合并文件
    JNIEXPORT void JNICALL
    native_merge(JNIEnv *env, jclass type, jstring mergeFilePath, jstring partPath, jint num) {
        //写入的合并文件
        const char *wirtePath = env->GetStringUTFChars(mergeFilePath, NULL);
        //拆分文件
        const char *readPath = env->GetStringUTFChars(partPath, NULL);
    
        //生成拆分文件绝对路径数组
        char partPaths[num][500];
        memset(partPaths, 0, sizeof(partPaths));
        for (int i = 0; i < num; i++) {
            sprintf(partPaths[i], readPath, i);
        }
    
        //写合并文件
        CFILE mergeFile = new CFILE(false);
        if (!mergeFile.Open(wirtePath, "w")) {
            jclass newExc = env->FindClass("java/lang/Exception");
            char info[500];
            memset(info, 0, 500);
            sprintf(info, "can not open :%s", mergeFilePath);
            if (newExc != NULL) env->ThrowNew(newExc, info);
            env->DeleteLocalRef(newExc);
    
            env->ReleaseStringUTFChars(mergeFilePath, wirtePath);
            env->ReleaseStringUTFChars(partPath, readPath);
            return;
        }
    
        for (int i = 0; i < num; i++) {
            CFILE partFile;
            partFile.Open(partPaths[i], "r");
            long partFileSize = partFile.GetFileSize();
            for (int j = 0; j < partFileSize; j++) {
                mergeFile.Fputc(partFile.Fgetc());
            }
        }
    
        env->ReleaseStringUTFChars(mergeFilePath, wirtePath);
        env->ReleaseStringUTFChars(partPath, readPath);
    }
    

    5.最后在activity中调用,实现效果,记得添加读写权限

            Button btn_click = findViewById(R.id.btn_click);
            btn_click.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FileDiffUtils.diff(Environment.getExternalStorageDirectory() + File.separator + "test.jpg", Environment.getExternalStorageDirectory() + File.separator + "test_%d.jpg", 4);
                }
            });
    
            Button btn_click2 = findViewById(R.id.btn_click2);
            btn_click2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FileDiffUtils.merge(Environment.getExternalStorageDirectory() + File.separator + "test_merge.jpg", Environment.getExternalStorageDirectory() + File.separator + "test_%d.jpg", 4);
                }
            });
    
    demo地址:https://gitee.com/aruba/NDKApplication.git

    相关文章

      网友评论

          本文标题:NDK--文件的拆分和合并

          本文链接:https://www.haomeiwen.com/subject/rfisvhtx.html