| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.base; |
| |
| import android.content.Context; |
| import android.net.Uri; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.util.List; |
| import java.util.Locale; |
| |
| /** |
| * Helper methods for dealing with Files. |
| */ |
| public class FileUtils { |
| private static final String TAG = "FileUtils"; |
| |
| /** |
| * Delete the given File and (if it's a directory) everything within it. |
| */ |
| public static void recursivelyDeleteFile(File currentFile) { |
| ThreadUtils.assertOnBackgroundThread(); |
| if (currentFile.isDirectory()) { |
| File[] files = currentFile.listFiles(); |
| if (files != null) { |
| for (File file : files) { |
| recursivelyDeleteFile(file); |
| } |
| } |
| } |
| |
| if (!currentFile.delete()) Log.e(TAG, "Failed to delete: " + currentFile); |
| } |
| |
| /** |
| * Delete the given files or directories by calling {@link #recursivelyDeleteFile(File)}. |
| * @param files The files to delete. |
| */ |
| public static void batchDeleteFiles(List<File> files) { |
| ThreadUtils.assertOnBackgroundThread(); |
| |
| for (File file : files) { |
| if (file.exists()) recursivelyDeleteFile(file); |
| } |
| } |
| |
| /** |
| * Extracts an asset from the app's APK to a file. |
| * @param context |
| * @param assetName Name of the asset to extract. |
| * @param dest File to extract the asset to. |
| * @return true on success. |
| */ |
| public static boolean extractAsset(Context context, String assetName, File dest) { |
| InputStream inputStream = null; |
| OutputStream outputStream = null; |
| try { |
| inputStream = context.getAssets().open(assetName); |
| outputStream = new BufferedOutputStream(new FileOutputStream(dest)); |
| byte[] buffer = new byte[8192]; |
| int c; |
| while ((c = inputStream.read(buffer)) != -1) { |
| outputStream.write(buffer, 0, c); |
| } |
| inputStream.close(); |
| outputStream.close(); |
| return true; |
| } catch (IOException e) { |
| if (inputStream != null) { |
| try { |
| inputStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| if (outputStream != null) { |
| try { |
| outputStream.close(); |
| } catch (IOException ex) { |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Atomically copies the data from an input stream into an output file. |
| * @param is Input file stream to read data from. |
| * @param outFile Output file path. |
| * @param buffer Caller-provided buffer. Provided to avoid allocating the same |
| * buffer on each call when copying several files in sequence. |
| * @throws IOException in case of I/O error. |
| */ |
| public static void copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer) |
| throws IOException { |
| File tmpOutputFile = new File(outFile.getPath() + ".tmp"); |
| try (OutputStream os = new FileOutputStream(tmpOutputFile)) { |
| Log.i(TAG, "Writing to %s", outFile); |
| |
| int count = 0; |
| while ((count = is.read(buffer, 0, buffer.length)) != -1) { |
| os.write(buffer, 0, count); |
| } |
| } |
| if (!tmpOutputFile.renameTo(outFile)) { |
| throw new IOException(); |
| } |
| } |
| |
| /** |
| * Returns a URI that points at the file. |
| * @param file File to get a URI for. |
| * @return URI that points at that file, either as a content:// URI or a file:// URI. |
| */ |
| public static Uri getUriForFile(File file) { |
| // TODO(crbug/709584): Uncomment this when http://crbug.com/709584 has been fixed. |
| // assert !ThreadUtils.runningOnUiThread(); |
| Uri uri = null; |
| |
| try { |
| // Try to obtain a content:// URI, which is preferred to a file:// URI so that |
| // receiving apps don't attempt to determine the file's mime type (which often fails). |
| uri = ContentUriUtils.getContentUriFromFile(file); |
| } catch (IllegalArgumentException e) { |
| Log.e(TAG, "Could not create content uri: " + e); |
| } |
| |
| if (uri == null) uri = Uri.fromFile(file); |
| |
| return uri; |
| } |
| |
| /** |
| * Returns the file extension, or an empty string if none. |
| * @param file Name of the file, with or without the full path. |
| * @return empty string if no extension, extension otherwise. |
| */ |
| public static String getExtension(String file) { |
| int index = file.lastIndexOf('.'); |
| if (index == -1) return ""; |
| return file.substring(index + 1).toLowerCase(Locale.US); |
| } |
| } |