版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_21983189/article/details/62886418
之前在开发中,遇到了一个问题,就是在项目assets下有一个apk需要在应用启动时安装到设备中,实现的思路是需要首先将apk拷贝到sdcard中,然后执行安装操作,还有一个问题就是项目中的数据库也需要放在sdcard中,这个apk大约有5M左右,我的数据库大约有30M左右,接下来我就按照普通的I/O流拷贝:
public static boolean copyDataToSD(Context context, String fileName, File strOutFile) {
InputStream myInput = null;
OutputStream myOutput = null;
try {
myOutput = new FileOutputStream(strOutFile);
myInput =context.getAssets().open(fileName);
byte[] buffer = new byte[1024];
int length = myInput.read(buffer);
while (length > 0) {
myOutput.write(buffer, 0, length);
length = myInput.read(buffer);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
if (myOutput != null) {
myOutput.flush();
myOutput.close();
}
if (myInput != null)
myInput.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
但是在执行上述代码时,却报错了,经过查询才知道android2.3之后要求从apk拷贝出去的数据必须小于1M,坑爹呀!!!
那么,我们只能将数据分解了!,直接看完整的代码:
public static List<String> splitBigFile(Context context, String fileName, String formatType, String dirName) {
//需要分割的文件的文件名称
String base = fileName;
//需要分割的文件的后缀名
String ext = formatType;
List<String> list = new ArrayList<>();
InputStream inputStream = null;
//以每个小文件1024*1024字节即1M的标准来分割
int split = 1024 * 1024;
byte[] buf = new byte[1024];
int num = 1;
try {
//需要分割的文件的文件
inputStream = context.getAssets().open(fileName + formatType);
File fileDir = new File(SDCardUtils.getSDCardPath() + File.separator + dirName);
if (!fileDir.exists()) {
fileDir.mkdir();
}
while (true) {
//以"demo"+num+".db"方式来命名小文件即分割后为demo1.db,demo2.db,。。。。。。
File file = new File(SDCardUtils.getSDCardPath() + File.separator + dirName + File.separator + base + num + ext);
if (!file.exists()) {
file.createNewFile();
}
list.add(file.getPath());
FileOutputStream fos = new FileOutputStream(file);
for (int i = 0; i < split / buf.length; i++) {
int read = inputStream.read(buf);
fos.write(buf, 0, read);
// 判断大文件读取是否结束
if (read < buf.length) {
inputStream.close();
fos.close();
return list;
}
}
num++;
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
public static boolean mergeFile(Object[] partFileList, String dst, String formatType, String dirName) {
try {
OutputStream out = new FileOutputStream(dst);
byte[] buffer = new byte[1024];
InputStream in;
int readLen = 0;
for (int i = 0; i < partFileList.length; i++) {
// 获得输入流 ,注意文件的路径
String str = partFileList[i].toString();
if (partFileList[i].toString().endsWith(formatType)) {
in = new FileInputStream(new File(partFileList[i].toString()));
while ((readLen = in.read(buffer)) != -1) {
out.write(buffer, 0, readLen);
}
out.flush();
in.close();
}
}
// 把所有小文件都进行写操作后才关闭输出流,这样就会合并为一个文件了
out.close();
deleteFolder(SDCardUtils.getSDCardPath() + File.separator + dirName);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static void deleteFolder(String folderPath) {
File localFolder = new File(folderPath);
if (localFolder.isDirectory()) {
String[] childrenStrings = localFolder.list();
int length = childrenStrings.length;
for (int i = 0; i < length; i++) {
File temp = new File(localFolder, childrenStrings[i]);
if (temp.isDirectory()) {
deleteFolder(temp.getName());
} else {
temp.delete();
}
}
localFolder.delete();
}
}
通过上面的代码可以看出实现的思路是:
a.将源文件平均1M大小地分解读取,然后每M流分别写入sdcard中一个小文件中,文件路径存放在一个集合中,这些小文件的命名分别问类似编号:xxx1.db,xxx2.db….等等;
b.在sdcard中创建一个要生成的目标文件,然后通过遍历这些小文件,将每个小文件写入目标文件中;
c.最后关闭流对象,删除这些小文件。
最后,在需要调用的地方:
//第一个参数是上下文,第二个参数是assets下数据包的名字,第三个参数表示数据包的后缀,第四个参数表示要在sdcard中临时存放分解的小文件的文件夹
List<String> strings =CopyDataToSDUtils.splitBigFile(MainActivity.this, "xxx", ".apk", "xxxapk");
boolean b =CopyDataToSDUtils.mergeFile(strings.toArray(), apkPath, ".apk", "hdpapk");
if (b) {
Log.d(MainActivity.class.getName(),"拷贝成功");
}else{
Log.d(MainActivity.class.getName(),"拷贝失败");
}
小结:以上是我个人在开发中的一个小插曲,通常我们在例如上传或下载大文件时,为了保证数据包能快速安全并且完整地到达,也会采用分包的处理方式。