Files
Aria/PublicComponent/src/main/java/com/arialyy/aria/util/FileUtil.java

746 lines
22 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.arialyy.aria.util;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.StatFs;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import android.text.TextUtils;
import com.arialyy.aria.core.AriaConfig;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.SequenceInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;
import java.util.regex.Pattern;
/**
* 文件操作工具类
*/
public class FileUtil {
private static final String TAG = "FileUtil";
private static final Pattern DIR_SEPORATOR = Pattern.compile("/");
private static final String EXTERNAL_STORAGE_PATH =
Environment.getExternalStorageDirectory().getPath();
/**
* 创建目录 当目录不存在的时候创建文件否则返回false
*/
public static boolean createDir(String path) {
File file = new File(path);
if (!file.exists()) {
if (!file.mkdirs()) {
ALog.d(TAG, "创建失败,请检查路径和是否配置文件权限!");
return false;
}
return true;
}
return false;
}
/**
* 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件
*
* @return {@code true} 创建成功、{@code false} 创建失败
*/
public static boolean createFile(String path) {
if (TextUtils.isEmpty(path)) {
ALog.e(TAG, "文件路径不能为null");
return false;
}
return createFile(new File(path));
}
/**
* 创建文件 当文件不存在的时候就创建一个文件。 如果文件存在,先删除原文件,然后重新创建一个新文件
*
* @return {@code true} 创建成功、{@code false} 创建失败
*/
public static boolean createFile(File file) {
if (file.getParentFile() == null || !file.getParentFile().exists()) {
ALog.d(TAG, "目标文件所在路径不存在,准备创建……");
if (!createDir(file.getParent())) {
ALog.d(TAG, "创建目录文件所在的目录失败!文件路径【" + file.getPath() + "");
}
}
// 文件存在,删除文件
deleteFile(file);
try {
if (file.createNewFile()) {
//ALog.d(TAG, "创建文件成功:" + file.getAbsolutePath());
return true;
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
return false;
}
/**
* 创建文件名如果url链接有后缀名则使用url中的后缀名
*
* @return url 的 hashKey
*/
public static String createFileName(String url) {
int end = url.indexOf("?");
String tempUrl, fileName = "";
if (end > 0) {
tempUrl = url.substring(0, end);
int tempEnd = tempUrl.lastIndexOf("/");
if (tempEnd > 0) {
fileName = tempUrl.substring(tempEnd + 1);
}
} else {
int tempEnd = url.lastIndexOf("/");
if (tempEnd > 0) {
fileName = url.substring(tempEnd + 1);
}
}
if (TextUtils.isEmpty(fileName)) {
fileName = CommonUtil.keyToHashKey(url);
}
return fileName;
}
/**
* 删除文件
*
* @param path 文件路径
* @return {@code true}删除成功、{@code false}删除失败
*/
public static boolean deleteFile(String path) {
if (TextUtils.isEmpty(path)) {
ALog.e(TAG, "删除文件失败,路径为空");
return false;
}
return deleteFile(new File(path));
}
/**
* 删除文件
*
* @param file 文件路径
* @return {@code true}删除成功、{@code false}删除失败
*/
public static boolean deleteFile(File file) {
if (file.exists()) {
final File to = new File(file.getAbsolutePath() + System.currentTimeMillis());
if (file.renameTo(to)) {
return to.delete();
} else {
return file.delete();
}
}
return false;
}
/**
* 将对象写入文件
*
* @param filePath 文件路径
* @param data data数据必须实现{@link Serializable}接口
*/
public static void writeObjToFile(String filePath, Object data) {
if (!(data instanceof Serializable)) {
ALog.e(TAG, "对象写入文件失败data数据必须实现Serializable接口");
return;
}
FileOutputStream ops = null;
try {
if (!createFile(filePath)) {
return;
}
ops = new FileOutputStream(filePath);
ObjectOutputStream oops = new ObjectOutputStream(ops);
oops.writeObject(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ops != null) {
try {
ops.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 从文件中读取对象
*
* @param filePath 文件路径
* @return 如果读取成功返回相应的Obj对象读取失败返回null
*/
public static Object readObjFromFile(String filePath) {
if (TextUtils.isEmpty(filePath)) {
ALog.e(TAG, "文件路径为空");
return null;
}
File file = new File(filePath);
if (!file.exists()) {
ALog.e(TAG, String.format("文件【%s】不存在", filePath));
return null;
}
FileInputStream fis = null;
try {
fis = new FileInputStream(filePath);
ObjectInputStream oois = new ObjectInputStream(fis);
return oois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 合并文件
*
* @param targetPath 目标文件
* @param subPaths 碎片文件路径
* @return {@code true} 合并成功,{@code false}合并失败
*/
public static boolean mergeFile(String targetPath, List<String> subPaths) {
File file = new File(targetPath);
FileOutputStream fos = null;
FileChannel foc = null;
try {
if (!file.exists()) {
file.createNewFile();
}
fos = new FileOutputStream(targetPath);
foc = fos.getChannel();
List<FileInputStream> streams = new LinkedList<>();
for (String subPath : subPaths) {
File f = new File(subPath);
if (!f.exists()) {
ALog.d(TAG, String.format("合并文件失败,文件【%s】不存在", subPath));
for (FileInputStream fis : streams) {
fis.close();
}
streams.clear();
return false;
}
streams.add(new FileInputStream(subPath));
}
Enumeration<FileInputStream> en = Collections.enumeration(streams);
SequenceInputStream sis = new SequenceInputStream(en);
ReadableByteChannel fic = Channels.newChannel(sis);
ByteBuffer bf = ByteBuffer.allocate(8196);
while (fic.read(bf) != -1) {
bf.flip();
foc.write(bf);
bf.compact();
}
fic.close();
sis.close();
return true;
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (foc != null) {
foc.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 分割文件
*
* @param filePath 被分割的文件路径
* @param num 分割的块数
*/
public static void splitFile(String filePath, int num) {
try {
final File file = new File(filePath);
long size = file.length();
FileInputStream fis = new FileInputStream(file);
FileChannel fic = fis.getChannel();
long j = 0;
long block = size / num;
for (int i = 0; i < num; i++) {
if (i == num - 1) {
block = size - block * (num - 1);
}
String subPath = file.getPath() + "." + i + ".part";
ALog.d(TAG, String.format("block = %s", block));
File subFile = new File(subPath);
if (!subFile.exists()) {
createFile(subFile);
}
FileOutputStream fos = new FileOutputStream(subFile);
FileChannel sfoc = fos.getChannel();
ByteBuffer bf = ByteBuffer.allocate(8196);
int len;
//fis.skip(block * i);
while ((len = fic.read(bf)) != -1) {
bf.flip();
sfoc.write(bf);
bf.compact();
j += len;
if (j >= block * (i + 1)) {
break;
}
}
ALog.d(TAG, String.format("len = %s", subFile.length()));
fos.close();
sfoc.close();
}
fis.close();
fic.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 检查SD内存空间是否充足
*
* @param filePath 文件保存路径
* @param fileSize 文件大小
* @return {@code false} 内存空间不足,{@code true}内存空间足够
*/
public static boolean checkSDMemorySpace(String filePath, long fileSize) {
List<String> dirs = FileUtil.getSDPathList(AriaConfig.getInstance().getAPP());
if (dirs == null || dirs.isEmpty()) {
return true;
}
for (String path : dirs) {
if (filePath.contains(path)) {
if (fileSize > 0 && fileSize > getAvailableExternalMemorySize(path)) {
return false;
}
}
}
return true;
}
/**
* sdcard 可用大小
*
* @param sdcardPath sdcard 根路径
* @return 单位为byte
*/
public static long getAvailableExternalMemorySize(String sdcardPath) {
StatFs stat = new StatFs(sdcardPath);
long blockSize = stat.getBlockSize();
long availableBlocks = stat.getAvailableBlocks();
return availableBlocks * blockSize;
}
/**
* sdcard 总大小
*
* @param sdcardPath sdcard 根路径
* @return 单位为byte
*/
public static long getTotalExternalMemorySize(String sdcardPath) {
StatFs stat = new StatFs(sdcardPath);
long blockSize = stat.getBlockSize();
long totalBlocks = stat.getBlockCount();
return totalBlocks * blockSize;
}
/**
* 获取SD卡目录列表
*/
public static List<String> getSDPathList(Context context) {
List<String> paths;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
paths = getVolumeList(context);
} else {
List<String> mounts = readMountsFile();
List<String> volds = readVoldFile();
paths = compareMountsWithVold(mounts, volds);
}
return paths;
}
/**
* 读取下载配置文件
*/
public static Properties loadConfig(File file) {
Properties properties = new Properties();
FileInputStream fis = null;
if (!file.exists()) {
createFile(file.getPath());
}
try {
fis = new FileInputStream(file);
properties.load(fis);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return properties;
}
/**
* 保存配置文件
*/
public static void saveConfig(File file, Properties properties) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file, false);
properties.store(fos, null);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (fos != null) {
fos.flush();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* getSDPathList
*/
private static List<String> getVolumeList(final Context context) {
List<String> pathList = new ArrayList<>();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StorageManager manager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
List<StorageVolume> volumes = manager.getStorageVolumes();
for (StorageVolume volume : volumes) {
String state = volume.getState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
pathList.add(volume.toString());
}
}
} else {
pathList.addAll(getStorageDirectories());
}
if (pathList.isEmpty()) {
pathList = new ArrayList<>();
pathList.add(EXTERNAL_STORAGE_PATH);
}
LinkedHashMap<Integer, String> paths = new LinkedHashMap<>();
for (String path : pathList) {
File root = new File(path);
if (!root.exists() || !root.isDirectory() || !canWrite(path)) {
continue;
}
//去除mount的相同目录
int key = (root.getTotalSpace() + "-" + root.getUsableSpace()).hashCode();
String prevPath = paths.get(key);
if (!TextUtils.isEmpty(prevPath) && prevPath.length() < path.length()) {
continue;
}
paths.put(key, path);
}
List<String> list = new ArrayList<>();
for (Integer key : paths.keySet()) {
list.add(paths.get(key));
}
return list;
}
private static boolean canWrite(String dirPath) {
File dir = new File(dirPath);
if (dir.canWrite()) {
return true;
}
boolean canWrite;
File testWriteFile = null;
try {
testWriteFile = new File(dirPath, "tw.txt");
if (testWriteFile.exists()) {
testWriteFile.delete();
}
testWriteFile.createNewFile();
FileWriter writer = new FileWriter(testWriteFile);
writer.write(1);
writer.close();
canWrite = true;
} catch (Exception e) {
e.printStackTrace();
canWrite = false;
} finally {
try {
if (testWriteFile != null && testWriteFile.exists()) {
testWriteFile.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return canWrite;
}
/**
* Raturns all available SD-Cards in the system (include emulated) Warning: Hack! Based on Android
* source code of version 4.3
* (API 18) Because there is no standard way to get it.
* 4.2+
*
* @return paths to all available SD-Cards in the system (include emulated)
*/
public static List<String> getStorageDirectories() {
// Final set of paths
final List<String> rv = new ArrayList<>();
// Primary physical SD-CARD (not emulated)
final String rawExternalStorage = System.getenv("EXTERNAL_STORAGE");
// All Secondary SD-CARDs (all exclude primary) separated by ":"
final String rawSecondaryStoragesStr = System.getenv("SECONDARY_STORAGE");
// Primary emulated SD-CARD
final String rawEmulatedStorageTarget = System.getenv("EMULATED_STORAGE_TARGET");
if (TextUtils.isEmpty(rawEmulatedStorageTarget)) {
// Device has physical external storage; use plain paths.
if (TextUtils.isEmpty(rawExternalStorage)) {
// EXTERNAL_STORAGE undefined; falling back to default.
rv.add("/storage/sdcard0");
} else {
rv.add(rawExternalStorage);
}
} else {
// Device has emulated storage; external storage paths should have
// userId burned into them.
final String rawUserId;
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
final String path = Environment.getExternalStorageDirectory().getAbsolutePath();
final String[] folders = DIR_SEPORATOR.split(path);
final String lastFolder = folders[folders.length - 1];
boolean isDigit = false;
if (!TextUtils.isEmpty(lastFolder) && TextUtils.isDigitsOnly(lastFolder)) {
isDigit = true;
}
rawUserId = isDigit ? lastFolder : "";
//} else {
// rawUserId = "";
//}
// /storage/emulated/0[1,2,...]
if (TextUtils.isEmpty(rawUserId)) {
rv.add(rawExternalStorage);
} else {
rv.add(rawEmulatedStorageTarget + File.separator + rawUserId);
}
}
// Add all secondary storages
if (!TextUtils.isEmpty(rawSecondaryStoragesStr)) {
// All Secondary SD-CARDs splited into array
final String[] rawSecondaryStorages = rawSecondaryStoragesStr.split(File.pathSeparator);
Collections.addAll(rv, rawSecondaryStorages);
}
// checkout SD-CARDs writable
for (int i = rv.size() - 1; i >= 0; i--) {
String path = rv.get(i);
File root = new File(path);
if (!root.exists() || !root.isDirectory() || !canWrite(path)) {
rv.remove(i);
}
}
return rv;
}
/**
* Scan the /proc/mounts file and look for lines like this: /dev/block/vold/179:1 /mnt/sdcard
* vfat
* rw,dirsync,nosuid,nodev,noexec ,relatime,uid=1000,gid=1015,fmask=0602,dmask=0602,allow_utime=0020,
* codepage=cp437,iocharset= iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0 When one is
* found, split it into its
* elements and then pull out the path to the that mount point and add it to the arraylist
*/
private static List<String> readMountsFile() {
// some mount files don't list the default
// path first, so we add it here to
// ensure that it is first in our list
List<String> mounts = new ArrayList<>();
mounts.add(EXTERNAL_STORAGE_PATH);
try {
Scanner scanner = new Scanner(new File("/proc/mounts"));
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (line.startsWith("/dev/block/vold/") || line.startsWith("/dev/block//vold/")) {//
String[] lineElements = line.split(" ");
// String partition = lineElements[0];
String element = lineElements[1];
// don't add the default mount path
// it's already in the list.
if (!element.equals(EXTERNAL_STORAGE_PATH)) {
mounts.add(element);
}
}
}
scanner.close();
} catch (Exception e) {
// e.printStackTrace();
}
return mounts;
}
private static List<String> readVoldFile() {
// read /etc/vold.conf or /etc/vold.fstab (it depends on version what
// clientConfig file is present)
List<String> vold = null;
Scanner scanner = null;
try {
try {
scanner = new Scanner(new File("/system/etc/vold.fstab"));
} catch (FileNotFoundException e1) {
// e1.printStackTrace();
scanner = new Scanner(new File("/system/etc/vold.conf"));
}
vold = new ArrayList<String>();
vold.add(EXTERNAL_STORAGE_PATH);
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (TextUtils.isEmpty(line)) {
continue;
}
line = line.trim();
if (line.startsWith("dev_mount")) {
String[] lineElements = line.split(" ");
if (lineElements.length < 3) {
continue;
}
String element = lineElements[2];
if (element.contains(":")) {
element = element.substring(0, element.indexOf(":"));
}
// ignore default path
if (!element.equals(EXTERNAL_STORAGE_PATH)) {
vold.add(element);
}
} else if (line.startsWith("mount_point")) {
String element = line.replaceAll("mount_point", "").trim();
if (!element.equals(EXTERNAL_STORAGE_PATH)) {
vold.add(element);
}
}
}
} catch (Exception e) {
// e.printStackTrace();
}
return vold;
}
private static List<String> compareMountsWithVold(List<String> mounts, List<String> volds) {
/*
* 有时候这两个list中的数据并不相同我们只需要取两个list的交集部分。
*/
for (int i = mounts.size() - 1; i >= 0; i--) {
String mount = mounts.get(i);
File root = new File(mount);
// 判断目录是否存在并且可读
if (!root.exists() || !root.isDirectory() || !root.canWrite()) {
mounts.remove(i);
continue;
}
if (volds != null && !volds.contains(mount)) {
mounts.remove(i);
}
}
// 清除无用数据
if (volds != null) {
volds.clear();
}
return mounts;
}
public static long getTotalMemory() {
String file_path = "/proc/meminfo";// 系统内存信息文件
String ram_info;
String[] arrayOfRam;
long initial_memory = 0L;
try {
FileReader fr = new FileReader(file_path);
BufferedReader localBufferedReader = new BufferedReader(fr, 8192);
// 读取meminfo第一行系统总内存大小
ram_info = localBufferedReader.readLine();
arrayOfRam = ram_info.split("\\s+");// 实现多个空格切割的效果
initial_memory =
Integer.valueOf(arrayOfRam[1]) * 1024;// 获得系统总内存单位是KB乘以1024转换为Byte
localBufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
return initial_memory;
}
public static long getAvailMemory(Context context) {
ActivityManager activityManager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo.availMem;
}
}