• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base;
6 
7 import android.content.Context;
8 import android.net.Uri;
9 
10 import java.io.BufferedOutputStream;
11 import java.io.File;
12 import java.io.FileOutputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.util.List;
17 import java.util.Locale;
18 
19 /**
20  * Helper methods for dealing with Files.
21  */
22 public class FileUtils {
23     private static final String TAG = "FileUtils";
24 
25     /**
26      * Delete the given File and (if it's a directory) everything within it.
27      */
recursivelyDeleteFile(File currentFile)28     public static void recursivelyDeleteFile(File currentFile) {
29         ThreadUtils.assertOnBackgroundThread();
30         if (currentFile.isDirectory()) {
31             File[] files = currentFile.listFiles();
32             if (files != null) {
33                 for (File file : files) {
34                     recursivelyDeleteFile(file);
35                 }
36             }
37         }
38 
39         if (!currentFile.delete()) Log.e(TAG, "Failed to delete: " + currentFile);
40     }
41 
42     /**
43      * Delete the given files or directories by calling {@link #recursivelyDeleteFile(File)}.
44      * @param files The files to delete.
45      */
batchDeleteFiles(List<File> files)46     public static void batchDeleteFiles(List<File> files) {
47         ThreadUtils.assertOnBackgroundThread();
48 
49         for (File file : files) {
50             if (file.exists()) recursivelyDeleteFile(file);
51         }
52     }
53 
54     /**
55      * Extracts an asset from the app's APK to a file.
56      * @param context
57      * @param assetName Name of the asset to extract.
58      * @param dest File to extract the asset to.
59      * @return true on success.
60      */
extractAsset(Context context, String assetName, File dest)61     public static boolean extractAsset(Context context, String assetName, File dest) {
62         InputStream inputStream = null;
63         OutputStream outputStream = null;
64         try {
65             inputStream = context.getAssets().open(assetName);
66             outputStream = new BufferedOutputStream(new FileOutputStream(dest));
67             byte[] buffer = new byte[8192];
68             int c;
69             while ((c = inputStream.read(buffer)) != -1) {
70                 outputStream.write(buffer, 0, c);
71             }
72             inputStream.close();
73             outputStream.close();
74             return true;
75         } catch (IOException e) {
76             if (inputStream != null) {
77                 try {
78                     inputStream.close();
79                 } catch (IOException ex) {
80                 }
81             }
82             if (outputStream != null) {
83                 try {
84                     outputStream.close();
85                 } catch (IOException ex) {
86                 }
87             }
88         }
89         return false;
90     }
91 
92     /**
93      * Atomically copies the data from an input stream into an output file.
94      * @param is Input file stream to read data from.
95      * @param outFile Output file path.
96      * @param buffer Caller-provided buffer. Provided to avoid allocating the same
97      *         buffer on each call when copying several files in sequence.
98      * @throws IOException in case of I/O error.
99      */
copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer)100     public static void copyFileStreamAtomicWithBuffer(InputStream is, File outFile, byte[] buffer)
101             throws IOException {
102         File tmpOutputFile = new File(outFile.getPath() + ".tmp");
103         try (OutputStream os = new FileOutputStream(tmpOutputFile)) {
104             Log.i(TAG, "Writing to %s", outFile);
105 
106             int count = 0;
107             while ((count = is.read(buffer, 0, buffer.length)) != -1) {
108                 os.write(buffer, 0, count);
109             }
110         }
111         if (!tmpOutputFile.renameTo(outFile)) {
112             throw new IOException();
113         }
114     }
115 
116     /**
117      * Returns a URI that points at the file.
118      * @param file File to get a URI for.
119      * @return URI that points at that file, either as a content:// URI or a file:// URI.
120      */
getUriForFile(File file)121     public static Uri getUriForFile(File file) {
122         // TODO(crbug/709584): Uncomment this when http://crbug.com/709584 has been fixed.
123         // assert !ThreadUtils.runningOnUiThread();
124         Uri uri = null;
125 
126         try {
127             // Try to obtain a content:// URI, which is preferred to a file:// URI so that
128             // receiving apps don't attempt to determine the file's mime type (which often fails).
129             uri = ContentUriUtils.getContentUriFromFile(file);
130         } catch (IllegalArgumentException e) {
131             Log.e(TAG, "Could not create content uri: " + e);
132         }
133 
134         if (uri == null) uri = Uri.fromFile(file);
135 
136         return uri;
137     }
138 
139     /**
140      * Returns the file extension, or an empty string if none.
141      * @param file Name of the file, with or without the full path.
142      * @return empty string if no extension, extension otherwise.
143      */
getExtension(String file)144     public static String getExtension(String file) {
145         int index = file.lastIndexOf('.');
146         if (index == -1) return "";
147         return file.substring(index + 1).toLowerCase(Locale.US);
148     }
149 }
150