一、文件拆分
先在Java层声明一个native方法
public static native void diff(String path, String patternPath, int fileNum);
其对应的调用(Kotlin)
private val SDK_CARD_PATH = Environment.getExternalStorageDirectory().absolutePath
val path = SDK_CARD_PATH + File.separator + "a.mp4"
val patternPath = SDK_CARD_PATH + File.separator + "a_%d.mp4"//%d可在native层用数字替代
FileUtils.diff(path, patternPath, 3)
接下来是 C 代码
获取文件大小
long getFileSize(char *__path) {
FILE *fp = fopen(__path, "rb");//打开一个文件,只允许读
fseek(fp, 0, SEEK_END);//游标从0到结束,表示整个文件
long size = ftell(fp);
fclose(fp);
return size;
}
//方法名已用动态注册
JNIEXPORT void JNICALL
jni_diff(JNIEnv *env, jclass type, jstring path_,
jstring patternPath_, jint fileNum) {
const char *path = (*env)->GetStringUTFChars(env, path_, 0);
const char *patternPath = (*env)->GetStringUTFChars(env, patternPath_, 0);
LogI("JNI Begin");
//申请一个二维数组用来存放子文件的绝对路径,指针可以用来表示数组,数组里的内容是char*,所以这里是二维数组
char **patchPathArray = malloc(sizeof(char *) * fileNum);
for (int i = 0; i < fileNum; ++i) {
//此处偷工减料一下,假设每个文件名的大小不会超过 sizeof(char) * 50
patchPathArray[i] = (char)malloc(sizeof(char*)*50);
//Java层传入的是地址 .../a_%d.mp4,此处会将文件命名为 .../a_0.mp4 , .../a_1.mp4 以此类推
sprintf(patchPathArray[i], patternPath,
i);
LogI("patch path : %s", patchPathArray[i]);//打印一下地址
}
long fileSize = getFileSize(path);
FILE *fpr = fopen(path, "rb");
if (fileSize % fileNum == 0) {//如果整除的情况下
long subFileSize = fileSize / fileNum;//每个子文件的大小
for (int i = 0; i < fileNum; i++) {
FILE *fpw = fopen(patchPathArray[i], "wb");//若文件不存在则创建,文件存在则覆写,只允许写
for (long j = 0; j < subFileSize; j++) {
fputc(fgetc(fpr), fpw);//fgetc方法:每次读取一个字节,光标就移动到该字节的尾部
}
fclose(fpw);
}
} else {//如果不整除的情况下
//一开始不用 fileNum-1 是为了避免 fileNum-1 的情况下发生整除而导致最后一个文件size为0
long subFileSize = fileSize / (fileNum - 1);
for (int i = 0; i < fileNum - 1; i++) {
FILE *fpw = fopen(patchPathArray[i], "wb");
for (int j = 0; j < subFileSize; j++) {
fputc(fgetc(fpr), fpw);
}
fclose(fpw);
}
long lastFileSize = fileSize % (fileNum - 1);
FILE *fpw = fopen(patchPathArray[fileNum - 1], "wb");
for (int i = 0; i < lastFileSize; ++i) {
fputc(fgetc(fpr), fpw);
}
fclose(fpw);
}
for (int i = 0; i < fileNum; i++) {
free(patchPathArray[i]);//释放所有路径的内存
}
free(patchPathArray);//释放二维数组的内存
fclose(fpr);
(*env)->ReleaseStringUTFChars(env, path_, path);
(*env)->ReleaseStringUTFChars(env, patternPath_, patternPath);
}
预先在SD卡根目录下放入a.mp4,执行完java层代码后(记得加入SD卡读写权限),可以获得a_0.mp4、a_1.mp4、a_2.mp4 等三个文件
二、文件合并
JNIEXPORT void JNICALL
jni_patch(JNIEnv *env, jclass type, jstring mergePath_,
jstring patternPath_, jint fileNum) {
const char *mergePath = (*env)->GetStringUTFChars(env, mergePath_, 0);
const char *patternPath = (*env)->GetStringUTFChars(env, patternPath_, 0);
char **pathArray = (char **) malloc(sizeof(char *) * fileNum);
for (int i = 0; i < fileNum; i++) {
pathArray[i] = (char *) malloc(sizeof(char) * 50);
sprintf(pathArray[i], patternPath, i);
}
FILE *fpw = fopen(mergePath, "wb");
for (int i = 0; i < fileNum; i++) {
long fileSize = getFileSize(pathArray[i]);
FILE *fpr = fopen(pathArray[i], "rb");
for (int j = 0; j < fileSize; j++) {
putc(getc(fpr), fpw);
}
fclose(fpr);
}
fclose(fpw);
for (int i = 0; i < fileNum; i++) {
free(pathArray[i]);
}
free(pathArray);
(*env)->ReleaseStringUTFChars(env, mergePath_, mergePath);
(*env)->ReleaseStringUTFChars(env, patternPath_, patternPath);
}