专栏首页android技术NDK--文件的拆分和合并

NDK--文件的拆分和合并

断点续传中,我们需要将一个文件拆分多个文件,并通过多线程上传,今天利用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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JNI--static静态修饰符和弱引用联合使用会出现的bug

    当static的修饰符出现是,表明该变量为静态变量,当我们为一个局部静态变量赋一个局部引用时(jstring,jclass,jint等),gc有可能会因为内存不...

    aruba
  • Android--NavigationView基本使用及源码分析

    aruba
  • JNI--局部引用,全局引用,弱全局引用

    aruba
  • Android NDK OpenCV级联方式实时进行人脸检测

    前面的文章《Android通过OpenCV和TesserartOCR实时进行识别》我们已经搭好一个利用NDK方式实时处理摄像头数据的程序了,今天我们就在看看Op...

    Vaccae
  • Linux系统编程:基本I/O系统调用

    Tencent JCoder
  • HBase 的MOB压缩分区策略介绍

    HBase应用场景非常广泛;社区前面有一系列文章。大家可以到社区看看看;张少华同学本篇主要讲HBase的MOB压缩分区策略介绍,非常赞!大力推荐!

    大数据和云计算技术
  • Linux中怎么实现文件的拆分和合并

    linux中: 文件的合并: 创建两个文件a, b :touch a b  cat a > b 是把a的内容写到b中,b中的内容会被覆盖 cat a >...

    武军超
  • Centos学习笔记--linux用户管理

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csd...

    流川疯
  • A2第8节枚举类型

    A2第8节枚举类型第二章第8节 1、有一些数据是开放性范围的,比如int、float、String。有些数据可选值是有限取值范围的,比如星座、月份名、方向,如...

    静心物语313
  • 数据部门如何All In AI

    通常,大数据部门会花费很大的力气构建数据平台,而这个数据平台除了能让研发,算法,分析师等角色爽一些,从宏观角度很大的节省部门人力成本,提高效率以外,似乎对公司/...

    用户1332428

扫码关注云+社区

领取腾讯云代金券