NDK学习笔记-文件的拆分与合并

文件的拆分与合并在开发中经常会用到,上传或是下载的时候都有这样的运用

文件拆分的思路

将文件大小拆分为n个文件
那么,每个文件的大小就是等大小的
如果文件大小被n除不尽,那么就使用n+1个文件来拆分
最后一个文件的大小就是整除不尽的那一部分数据

文件合并的思路

将拆分出来的全部文件胺顺序读取
挨个数据写入到指定文件中
所有文件数据写入完毕
那么合并就完成了

代码实现

布局文件(activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="拆分"
        android:onClick="mDiff" />
    
    <Button 
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="合并"
        android:onClick="mPatch"/>

</LinearLayout>

主活动文件(MainActivity.java

import java.io.File;

import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

	private String SD_CARD_PATH;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		SD_CARD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath();
	}
	
	public void mDiff(View v) {
		String path = SD_CARD_PATH + File.separatorChar + "test.mp3";
		String path_pattern = SD_CARD_PATH + File.separatorChar + "test_%d.mp3";
		Utils.diff(path, path_pattern, 3);
		Toast.makeText(MainActivity.this, "···拆分完成···", Toast.LENGTH_SHORT).show();
		Log.d("cj5785","···拆分完成···");
	}
	
	public void mPatch(View v) {
		String path_pattern = SD_CARD_PATH + File.separatorChar + "test_%d.mp3";
		String path_merge = SD_CARD_PATH + File.separatorChar + "test_merge.mp3";
		Utils.patch(path_pattern, path_merge, 3);
		Toast.makeText(MainActivity.this, "···合并完成···", Toast.LENGTH_SHORT).show();
		Log.d("cj5785","···合并完成···");
	}
}

工具类文件(Utils.java

public class Utils {

	/**
	 * 拆分
	 * @param path 原始文件路径
	 * @param path_pattern 拆分文件路径
	 * @param count 拆分个数
	 */
	public native static void diff(String path, String path_pattern, int count);
	
	/**
	 * 合并
	 * @param path_pattern 拆分文件路径
	 * @param path_merge 合并文件路径
	 * @param count 拆分的文件个数
	 */
	public native static void patch(String path_pattern, String path_merge, int count);
	
	static {
		System.loadLibrary("NdkFilePatch");
	}
	
}

JNI头文件(com_cj5785_ndkfilepatch_Utils.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cj5785_ndkfilepatch_Utils */

#ifndef _Included_com_cj5785_ndkfilepatch_Utils
#define _Included_com_cj5785_ndkfilepatch_Utils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_cj5785_ndkfilepatch_Utils
 * Method:    diff
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_cj5785_ndkfilepatch_Utils_diff
  (JNIEnv *, jclass, jstring, jstring, jint);

/*
 * Class:     com_cj5785_ndkfilepatch_Utils
 * Method:    patch
 * Signature: (Ljava/lang/String;Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_com_cj5785_ndkfilepatch_Utils_patch
  (JNIEnv *, jclass, jstring, jstring, jint);

#ifdef __cplusplus
}
#endif
#endif

JNI头文件实现(NdkFilePatch.c

#include <stdlib.h>
#include <stdio.h>
#include <Android/log.h>

#include "com_cj5785_ndkfilepatch_Utils.h"

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,"cj5785",__VA_ARGS__)

//获取文件大小
long get_file_size(char const *path)
{
	FILE *fp = fopen(path,"rb");
	fseek(fp,0,SEEK_END);
	long size = ftell(fp);
	fclose(fp);
	return size;
}

//拆分
JNIEXPORT void JNICALL Java_com_cj5785_ndkfilepatch_Utils_diff
  (JNIEnv *env, jclass jcls, jstring path_jstr, jstring path_pattern_jstr, jint file_num)
{
	//文件路径
	const char *path = (*env)->GetStringUTFChars(env,path_jstr,NULL);
	const char *path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL);

	//拆分完成后的子文件路径
	char **patches = (char **)malloc(sizeof(char *) * file_num);
	memset(patches, 0, sizeof(char *) * file_num);
	int i = 0;
	for(;i < file_num;i++)
	{
		patches[i] = (char *)malloc(sizeof(char) * 100);
		memset(patches[i], 0, sizeof(char) * 100);
		//子文件名称
		sprintf(patches[i], path_pattern, i+1);
		LOGI("patch path:%s",patches[i]);
	}

	//读取path文件,写入到file_num个文件中
	int file_size = get_file_size(path);
	FILE *fpr = fopen(path, "rb");
	//文件大小能被整除
	if(file_size % file_num == 0)
	{
		int part = file_size / file_num;
		i = 0;
		for(;i < file_num; i++)
		{
			FILE *fpw = fopen(patches[i], "wb");
			int j = 0;
			for(;j < part; j++)
			{
				fputc(fgetc(fpr),fpw);
			}
			fclose(fpw);
		}
	}else{
		int part = file_size / (file_num - 1);
		i = 0;
		for(;i < file_num - 1; i++)
		{
			FILE *fpw = fopen(patches[i], "wb");
			int j = 0;
			for(;j < part; j++)
			{
				fputc(fgetc(fpr),fpw);
			}
			fclose(fpw);
		}
		FILE *fpw = fopen(patches[file_num - 1], "wb");
		i = 0;
		for(;i < file_size % (file_num -1); i++)
		{
			fputc(fgetc(fpr),fpw);
		}
		fclose(fpw);
	}
	fclose(fpr);
	//释放malloc的空间
	i = 0;
	for(;i < file_num; i++)
	{
		free(patches[i]);
	}
	free(patches);
	patches = NULL;
	//释放资源
	(*env)->ReleaseStringUTFChars(env,path_jstr,path);
	(*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern);
}

//合并
JNIEXPORT void JNICALL Java_com_cj5785_ndkfilepatch_Utils_patch
  (JNIEnv *env, jclass jcls, jstring path_pattern_jstr, jstring path_merge_jstr, jint file_num)
{
	//文件路径
	const char *path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL);
	const char *path_merge = (*env)->GetStringUTFChars(env,path_merge_jstr,NULL);

	//子文件路径列表
	char **patches = (char **)malloc(sizeof(char *) * file_num);
	memset(patches, 0, sizeof(char *) * file_num);
	int i = 0;
	for(; i < file_num; i++)
	{
		patches[i] = (char *)malloc(sizeof(char) * 100);
		memset(patches[i], 0, sizeof(char) * 100);
		sprintf(patches[i], path_pattern, i+1);
		LOGI("patch path:%s", patches[i]);
	}
	FILE *fpw = fopen(path_merge, "wb");
	i = 0;
	for(; i < file_num; i++)
	{
		int file_size = get_file_size(patches[i]);
		FILE *fpr = fopen(patches[i], "rb");
		int j = 0;
		for(; j < file_size; j++)
		{
			fputc(fgetc(fpr),fpw);
		}
		fclose(fpr);
	}
	fclose(fpw);
	//释放malloc的空间
	i = 0;
	for(; i < file_num; i++)
	{
		free(patches[i]);
	}
	free(patches);
	patches = NULL;
	//释放资源
	(*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern);
	(*env)->ReleaseStringUTFChars(env,path_merge_jstr,path_merge);
}

Android.mk文件

因为在C实现代码中使用了日志打印,所以要在Android.mk文件中,添加日志打印的依赖

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := NdkFilePatch
LOCAL_SRC_FILES := NdkFilePatch.c
LOCAL_LDLIBS := -lm -llog

include $(BUILD_SHARED_LIBRARY)

问题总结

  • 在最开始的时候,日志无法打印,报错ANDROID_LOG_INFO不存在,添加本地支持以后仍然不行,无论怎么折腾,依旧如此。无奈之下,重启eclipse,居然好了。后经查阅得知,这是NDK r9d存在的bug,按照stackoverflow一位回答者的建议,先clean项目,然后build就好了:

Cleaning the project and Project -> Build Project (I have Build Automatically disabled) recreated the .so library and all the symbols are now properly found

  • 在拆分实现的时候,无论何种情况,得到的第最后一个拆分文件大小都为零,这里是因为在拆分的时候,大小的计算是基于字节大小的,故最后一个文件存储的大小是除数的字节数大小以下的一个值,很小。。。
原文地址:https://www.cnblogs.com/cj5785/p/10664672.html