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

文件拆分的思路

将文件大小拆分为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

  • 在拆分实现的时候,无论何种情况,得到的第最后一个拆分文件大小都为零,这里是因为在拆分的时候,大小的计算是基于字节大小的,故最后一个文件存储的大小是除数的字节数大小以下的一个值,很小。。。

NDK学习笔记-文件的拆分与合并的更多相关文章

  1. C#文件的拆分与合并操作示例

    C#文件的拆分与合并操作示例代码. 全局变量定义 ;//文件大小 //拆分.合并的文件数 int count; FileInfo splitFile; string splitFliePath; Fi ...

  2. 《python基础教程(第二版)》学习笔记 文件和素材(第11章)

    <python基础教程(第二版)>学习笔记 文件和素材(第11章) 打开文件:open(filename[,mode[,buffering]]) mode是读写文件的模式f=open(r' ...

  3. [Python学习笔记]文件的读取写入

    文件与文件路径 路径合成 os.path.join() 在Windows上,路径中以倒斜杠作为文件夹之间的分隔符,Linux或OS X中则是正斜杠.如果想要程序正确运行于所有操作系统上,就必须要处理这 ...

  4. python学习笔记:文件操作和集合(转)

    转自:http://www.nnzhp.cn/article/16/ 这篇博客来说一下python对文件的操作. 对文件的操作分三步: 1.打开文件获取文件的句柄,句柄就理解为这个文件 2.通过文件句 ...

  5. NDK学习笔记(四):OutputContext机制

    首先NDK文档中的Op.h头文件中已经有了相关概念的解释,摘录翻译如下: /*! \fn const OutputContext& Op::outputContext() const; The ...

  6. NDK学习笔记(三):DynamicKnobs的机制

    最近的NDK开发涉及到了动态input及动态knobs的问题. 开发需求如下:建立一个节点,该节点能获取每一个input上游的inputframerange信息. 具体下来就是:需要Node的inpu ...

  7. NDK学习笔记-增量更新

    虽然现在有插件化开发和热修复,但为何还需要增量更新?插件化开发和热修复依赖于宿主程序,增量更新适合更新宿主程序. 差分包生成的前提 差分包的生成依赖于BsDiff开源项目,而BsDiff又依赖于Bzi ...

  8. .net学习笔记--文件读写的几种方式

    在.net中有很多有用的类库来读写硬盘上的文件 一般比较常用的有: File:1.什么时候使用:当读写件大小不大,同时可以一次性进行读写操作的时候使用         2.不同的方式可以读写文件类型不 ...

  9. Python学习笔记——文件

    1.文件只是连续的字节序列 open()内建函数是打开文件之门的钥匙 file_obj=open(file_name,access_mode='r/w/a,' buffering=-1) file_n ...

随机推荐

  1. node.js中允许的app对象声明方式

    伪对象形式 app = function () { console.log("我是一个初始化的app对象"); }; app.get=function () { console.l ...

  2. mybatis+redis实现二级缓存

    在网上看了很多资料,发现例子都是千篇一律的相互复制.而且,使用的都是jedis的客户端..我这里使用的是redistemplate类实现. 缓存的原理..实现cache类接口,当哪个类需要缓存的时候, ...

  3. C# 未能加载项目文件

    在使用VS打开从网上下载或者从其他地方复制得来的解决方案时,经常会出现这样一个错误,"在解决方案中的一个或多个项目由于以下原因未能加载项目文件或网站已移动或已重命名,或者不在您的计算机上.& ...

  4. 022_统计每个远程 IP 访问了本机 apache 几次?

    #$1为IP#i为不同的IP#print ip[i],i 输出不同IP出现的次数总和以及它们是谁#ip[$1]++ 未定义则从0开始,IP出现则自增 #!/bin/bashawk '{ip[$1]++ ...

  5. SpringMVC--DispatcherServlet

    DispatcherServlet 是前端控制器设计模式的实现,提供 Spring Web MVC 的集中访问点,而且负责职责的分派,而且与 Spring IoC 容器无缝集成,从而可以获得 Spri ...

  6. Apache Flink - 分布式运行环境

    1.任务和操作链 下面的数据流图有5个子任务执行,因此有五个并行线程. 2.Job Managers, Task Managers, Clients Job Managers:协调分布式运行,他们安排 ...

  7. 【Robot Framework 项目实战 03】使用脚本自动生成统一格式的RF自动化用例

    背景 虽然大家都已经使用了统一的关键字,但是在检查了一些测试用例之后,还是发现因为大家对RF的熟悉程度不一导致的测试用例颗粒度差异很大的情况:而且在手动方式转化测试用例过程中,有不少工作是完全重复的且 ...

  8. Java并发指南9:AQS共享模式与并发工具类的实现

    一行一行源码分析清楚 AbstractQueuedSynchronizer (三) 转自:https://javadoop.com/post/AbstractQueuedSynchronizer-3 ...

  9. k-means和iosdata聚类算法在生活案例中的运用

    引言:聚类是将数据分成类或者簇的过程,从而使同簇的对象之间具有很高的相似度,而不同的簇的对象相似度则存在差异.聚类技术是一种迭代重定位技术,在我们的生活中也得到了广泛的运用,比如:零件分组.数据评价. ...

  10. 调用subprocess 使用logging打印日志

    #!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Henry 17607168727@163.com import sys import lo ...