前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NDK--文件的拆分和合并

NDK--文件的拆分和合并

作者头像
aruba
发布2020-07-03 13:58:34
6260
发布2020-07-03 13:58:34
举报
文章被收录于专栏:android技术android技术
断点续传中,我们需要将一个文件拆分多个文件,并通过多线程上传,今天利用JNI实现文件的拆分和合并,调用c/c++的方式,性能会有所提升。
1.创建文件工具类
这边将文件封装成c++类,减少了繁琐的操作

_FileClass.h

代码语言:javascript
复制
//
// 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

代码语言:javascript
复制
//
// 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方法
代码语言:javascript
复制
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方法
代码语言:javascript
复制
//文件拆分和合并
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方法中调用
代码语言:javascript
复制
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.实现文件拆分和合并方法
代码语言:javascript
复制
//二进制一个个字节写文件
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中调用,实现效果,记得添加读写权限
代码语言:javascript
复制
        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
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 断点续传中,我们需要将一个文件拆分多个文件,并通过多线程上传,今天利用JNI实现文件的拆分和合并,调用c/c++的方式,性能会有所提升。
  • 1.创建文件工具类
    • 这边将文件封装成c++类,减少了繁琐的操作
      • 注意:记得在CMakeList中添加
      • 2.创建Java工具类,定义文件拆分和合并的native方法
      • 3.以动态注册的方式连接native方法
        • JNI_OnLoad方法中调用
        • 4.实现文件拆分和合并方法
        • 5.最后在activity中调用,实现效果,记得添加读写权限
          • demo地址:https://gitee.com/aruba/NDKApplication.git
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档