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