/* * Copyright (c) 2014-2015 Sean Liu. * * Licensed 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.github.baoti.pioneer.misc.util; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.StatFs; import android.support.annotation.NonNull; import com.github.baoti.pioneer.data.DataConstants; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import timber.log.Timber; /** * IO相关 * <p/> * Created by sean on 2014/10/12. * * TODO support Android 6.0 (*_EXTERNAL_STORAGE permissions). */ public class IoUtils { public static boolean saveFile(File file, byte[] content) { FileOutputStream outputStream; try { outputStream = openFileOutputStream(file); } catch (FileNotFoundException e) { Timber.d(e, "[saveFile] Couldn't open output"); return false; } boolean saveOk = true; try { outputStream.write(content); } catch (IOException e) { Timber.d(e, "[saveFile] Couldn't write"); return false; } finally { if (!close(outputStream)) { saveOk = false; } } if (!saveOk) { //noinspection ResultOfMethodCallIgnored file.delete(); } return saveOk; } public static boolean saveBitmap(File file, Bitmap bitmap, Bitmap.CompressFormat format, int quality) { FileOutputStream outputStream; try { outputStream = openFileOutputStream(file); } catch (FileNotFoundException e) { Timber.d(e, "[saveBitmap] Couldn't open file output"); return false; } boolean saveOk = true; try { bitmap.compress(format, quality, outputStream); } finally { if (!close(outputStream)) { saveOk = false; } } if (!saveOk) { //noinspection ResultOfMethodCallIgnored file.delete(); } return saveOk; } public static boolean saveBitmap(ContentResolver contentResolver, Uri uri, Bitmap bitmap, Bitmap.CompressFormat format, int quality) { OutputStream outputStream; try { outputStream = contentResolver.openOutputStream(uri); } catch (FileNotFoundException e) { Timber.d(e, "[saveBitmap] Couldn't open uri output"); return false; } boolean saveOk = true; try { bitmap.compress(format, quality, outputStream); } finally { if (!close(outputStream)) { saveOk = false; } } if (!saveOk) { contentResolver.delete(uri, null, null); } return saveOk; } public static boolean copyAssets(AssetManager assetManager, String assetsPath, File dst) { InputStream is; try { is = assetManager.open(assetsPath); } catch (IOException e) { Timber.d(e, "[copyAssets] Couldn't open assets"); return false; } try { return copyStream(is, dst); } finally { close(is); } } public static boolean copyFile(File src, File dst) { FileInputStream fis; try { fis = new FileInputStream(src); } catch (FileNotFoundException e) { Timber.d(e, "[copyFile] Couldn't open input"); return false; } try { return copyStream(fis, dst); } finally { close(fis); } } public static boolean copyFile(ContentResolver contentResolver, Uri src, File dst) { InputStream inputStream; try { inputStream = contentResolver.openInputStream(src); } catch (FileNotFoundException e) { Timber.d(e, "[copyFile] Couldn't open uri input"); return false; } try { return copyStream(inputStream, dst); } finally { close(inputStream); } } public static boolean copyStream(InputStream src, File dst) { FileOutputStream fos; try { fos = openFileOutputStream(dst); } catch (FileNotFoundException e) { Timber.d(e, "[copyStream] Couldn't open output"); return false; } boolean copyOk = true; try { copyStream(src, fos); } catch (IOException e) { copyOk = false; Timber.d(e, "[copyStream] Couldn't copy"); } finally { if (!close(fos)) { copyOk = false; } } if (!copyOk) { Timber.d("[copyStream] Copy failed"); //noinspection ResultOfMethodCallIgnored dst.delete(); } return copyOk; } public static void copyStream(InputStream src, OutputStream dst) throws IOException { byte[] buffer = new byte[1024]; int len; while ((len = src.read(buffer)) != -1) { dst.write(buffer, 0, len); } } public static FileOutputStream openFileOutputStream(File file) throws FileNotFoundException { ensureDirsExist(file.getParentFile()); return new FileOutputStream(file); } public static boolean close(Closeable closeable) { if (closeable != null) { try { closeable.close(); } catch (IOException e) { Timber.d(e, "[close] Close failed"); return false; } } return true; } public static String readString(File file) { FileInputStream fis; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { Timber.d(e, "[readString] Couldn't open input: %s", file); return ""; } try { return readString(fis); } catch (Exception e) { Timber.d(e, "[readString] Couldn't read input: %s", file); } finally { close(fis); } return ""; } /** * 从InputStream中读取String,该函数不执行关闭操作 * * @param in InputStream * @return String * @throws Exception */ public static String readString(InputStream in) throws Exception { return new String(toByteArray(in), "UTF-8"); } public static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024 * 4]; int n; while (-1 != (n = input.read(buffer))) { byteArrayOutputStream.write(buffer, 0, n); } return byteArrayOutputStream.toByteArray(); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private static long getExtStorageAvailableApi18() { if (hasExtStorage()) { File extStorage = Environment.getExternalStorageDirectory(); StatFs sf = new StatFs(extStorage.getAbsolutePath()); return sf.getAvailableBlocksLong() * sf.getBlockSizeLong(); } return 0; } @SuppressWarnings("deprecation") private static long getExtStorageAvailableSmallerThanApi18() { if (hasExtStorage()) { File extStorage = Environment.getExternalStorageDirectory(); StatFs sf = new StatFs(extStorage.getAbsolutePath()); return ((long) sf.getAvailableBlocks()) * sf.getBlockSize(); } return 0; } public static long getExtStorageAvailable() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { return getExtStorageAvailableApi18(); } else { return getExtStorageAvailableSmallerThanApi18(); } } public static boolean hasExtStorage() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } public static boolean rmFile(File file) { return !file.exists() || (file.isFile() && file.delete()); } /** * rm -r */ public static boolean rmR(File file) { if (!file.exists()) { return true; } boolean result = true; if (file.isDirectory()) { for (File subFile : file.listFiles()) { result &= rmR(subFile); } } else { result = file.delete(); } return result; } public static long getTotalLength(File file) { if (file.isDirectory()) { long total = 0; for (File child : file.listFiles()) { total += getTotalLength(child); } return total; } else { return file.length(); } } /** * 保证目录存在 * * @param dir * @return */ public static boolean ensureDirsExist(File dir) { if (!dir.isDirectory()) { if (!dir.mkdirs()) { // recheck existence in case of cross-process race if (!dir.isDirectory()) { return false; } } } return true; } /** * 获得在文件存储中的文件 */ public static File fromFilesDir(Context context, String path) { if (path != null && path.startsWith(File.separator)) { path = path.substring(File.separator.length()); } File filesDir = context.getExternalFilesDir(null); File rollback = filesDir; if (filesDir != null && !ensureDirsExist(filesDir)) { filesDir = null; } if (filesDir == null) { filesDir = context.getFilesDir(); } if (filesDir == null) { filesDir = rollback; } if (path == null) { return filesDir; } else { return new File(filesDir, path); } } /** * 获得在缓存存储中的文件 * * @param path * @return */ public static File fromCacheDir(Context context, String path) { if (path != null && path.startsWith(File.separator)) { path = path.substring(File.separator.length()); } File cacheDir = context.getExternalCacheDir(); File rollback = cacheDir; if (cacheDir != null && !ensureDirsExist(cacheDir)) { cacheDir = null; } if (cacheDir == null) { // maybe null cacheDir = context.getCacheDir(); } if (cacheDir == null) { cacheDir = rollback; } if (path == null) { return cacheDir; } else { return new File(cacheDir, path); } } /** * 是否应用私有的文件 * * @param file * @return */ public static boolean isAppPrivate(Context context, File file) { return isInDataDir(context, file); } /** * 文件是否在 内部 Data 目录下 * * @param file * @return */ public static boolean isInDataDir(Context context, File file) { if (file == null) { return false; } String dataDir = context.getApplicationInfo().dataDir; if (dataDir != null) { dataDir = new File(dataDir).getAbsolutePath(); // Removing any trailing slash String filePath = file.getAbsolutePath(); return filePath.equals(dataDir) || filePath.startsWith(dataDir + File.separator); } return false; } @NonNull public static File getDCIMDirectory(String type) { File directory = getAvailableExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); return new File(directory, type); } @NonNull public static File getAvailableExternalStoragePublicDirectory(String type) { File result = Environment.getExternalStoragePublicDirectory(type); if (ensureDirsExist(result)) { return result; } Timber.v("Fail to create public storage directory: " + result); return result; } @NonNull public static File fromPublicPath(Context context, String path) { String publicBase = DataConstants.DIRECTORY_PUBLIC_BASE; if (hasExtStorage()) { File result = new File(Environment.getExternalStorageDirectory(), publicBase); if (ensureDirsExist(result)) { if (path == null) { return result; } result = new File(result, path); ensureDirsExist(result.getParentFile()); return result; } } File result = fromFilesDir(context, publicBase); ensureDirsExist(result); if (path != null) { result = new File(result, path); ensureDirsExist(result.getParentFile()); } return result; } /** * 以当前时间为名称, 生成文件 * @param directory 目录 * @param suffix 后缀名 * @return 生成的文件 */ @NonNull public static File generateDatedFile(File directory, String suffix) { return generateDatedFile(directory, suffix, false, false); } /** * 以当前时间为名称, 生成文件 * @param directory 目录 * @param suffix 后缀名 * @param twoStage 是否生成二级文件, 即存放在当前月的文件夹下 * @return 生成的文件 */ @NonNull public static File generateDatedFile(File directory, String suffix, boolean twoStage) { return generateDatedFile(directory, suffix, twoStage, false); } /** * 以当前时间为名称, 生成文件 * @param directory 目录 * @param suffix 后缀名 * @param twoStage 是否生成二级文件, 即存放在当前月的文件夹下 * @param createNewFile 是否需要创建新文件 * @return 生成的文件 */ @NonNull public static File generateDatedFile(File directory, String suffix, boolean twoStage, boolean createNewFile) { boolean dirOk = ensureDirsExist(directory); if (!dirOk) { Timber.v("Fail to create directory: " + directory); } String filename = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()); if (twoStage) { String month = filename.substring(0, 6); return generateDatedFile(new File(directory, month), suffix, false); } File result = new File(directory, filename + suffix); if (!createNewFile) { return result; } int i = -1; try { while (!result.createNewFile()) { result = new File(directory, filename + (i + suffix)); i--; } } catch (IOException e) { Timber.v(e, "Fail to create new file: " + result); } return result; } @NonNull public static File createTempFile(Context context, String suffix, boolean isPublic) throws IOException { File directory; if (isPublic) { directory = null; } else { directory = fromCacheDir(context, DataConstants.Temp.TMP_DIRECTORY_NAME); if (directory != null && !ensureDirsExist(directory)) { Timber.w("Could not create tmp directory in cacheDir"); directory = null; } } File tmp = File.createTempFile(DataConstants.Temp.TMP_FILE_PREFIX, suffix, directory); Timber.v("Create tmp file: %s", tmp); if (isPublic) { setOtherWritable(tmp); } return tmp; } private static void setOtherWritable(File file) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { boolean succeeded = file.setWritable(true, false); if (!succeeded) { Timber.d("Fail to setWritable(true, false): %s", file); } } } public static final String SCHEME_FILE = "file"; public static boolean isFile(Uri uri) { return uri != null && SCHEME_FILE.equals(uri.getScheme()); } public static File toFile(Uri uri) { if (uri != null && isFile(uri)) { return new File(uri.getPath()); } return null; } }