• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 package com.ohos.hapsigntool.utils;
17 
18 import com.google.gson.Gson;
19 import com.google.gson.GsonBuilder;
20 import com.ohos.hapsigntool.error.CustomException;
21 import com.ohos.hapsigntool.error.ERROR;
22 import com.ohos.hapsigntool.error.SignToolErrMsg;
23 
24 import java.io.BufferedWriter;
25 import java.io.ByteArrayOutputStream;
26 import java.io.Closeable;
27 import java.io.DataOutputStream;
28 import java.io.File;
29 import java.io.FileInputStream;
30 import java.io.FileNotFoundException;
31 import java.io.FileOutputStream;
32 import java.io.IOException;
33 import java.io.InputStream;
34 import java.io.OutputStream;
35 import java.io.OutputStreamWriter;
36 import java.nio.charset.Charset;
37 import java.nio.file.Files;
38 
39 /**
40  * Common file operation.
41  *
42  * @since 2021/12/28
43  */
44 public final class FileUtils {
45     /**
46      * LOGGER.
47      */
48     private static final LogUtils LOGGER = new LogUtils(FileUtils.class);
49 
50     /**
51      * add GSON static.
52      */
53     public static final Gson GSON = (new GsonBuilder()).disableHtmlEscaping().create();
54 
55     /**
56      * add GSON_PRETTY_PRINT static.
57      */
58     public static final Gson GSON_PRETTY_PRINT = (new GsonBuilder()).disableHtmlEscaping().setPrettyPrinting().create();
59 
60     /**
61      * File reader block size
62      */
63     public static final int FILE_BUFFER_BLOCK = 1024 * 1024;
64 
65     /**
66      * File end
67      */
68     public static final int FILE_END = -1;
69 
70     /**
71      * Expected split string length
72      */
73     public static final int SPLIT_LENGTH = 2;
74 
75     /**
76      * libs dir path
77      */
78     public static final String LIBS_PATH_PREFIX = "libs/";
79 
80     /**
81      * abc file suffix
82      */
83     public static final String ABC_FILE_SUFFIX = ".abc";
84 
85     /**
86      * an file suffix
87      */
88     public static final String NATIVE_LIB_AN_SUFFIX = ".an";
89 
90     /**
91      * bitmap file name
92      */
93     public static final String BIT_MAP_FILENAME = ".pages.info";
94 
95     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
96 
FileUtils()97     private FileUtils() {
98     }
99 
100     /**
101      * Close closeable quietly.
102      *
103      * @param closeable closeable
104      */
close(Closeable closeable)105     public static void close(Closeable closeable) {
106         if (closeable != null) {
107             try {
108                 closeable.close();
109             } catch (IOException exception) {
110                 LOGGER.debug(exception.getMessage(), exception);
111             }
112         }
113     }
114 
115     /**
116      * Read byte from input file.
117      *
118      * @param file Which file to read
119      * @return byte content
120      * @throws IOException Read failed
121      */
readFile(File file)122     public static byte[] readFile(File file) throws IOException {
123         return read(Files.newInputStream(file.toPath()));
124     }
125 
126     /**
127      * Read byte from input stream.
128      *
129      * @param input Input stream
130      * @return File content
131      * @throws IOException Read failed
132      */
read(InputStream input)133     public static byte[] read(InputStream input) throws IOException {
134         try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
135             byte[] buffer = new byte[FILE_BUFFER_BLOCK];
136             int read;
137             while ((read = input.read(buffer)) != FILE_END) {
138                 output.write(buffer, 0, read);
139             }
140             return output.toByteArray();
141         } finally {
142             close(input);
143         }
144     }
145 
146     /**
147      * Read byte from input file.
148      *
149      * @param file input file
150      * @param offset offset
151      * @param length length
152      * @return data bytes
153      */
readFileByOffsetAndLength(File file, long offset, long length)154     public static byte[] readFileByOffsetAndLength(File file, long offset, long length) throws IOException {
155         try (FileInputStream input = new FileInputStream(file)) {
156             return readInputByOffsetAndLength(input, offset, length);
157         }
158     }
159 
160     /**
161      * Read byte from input stream.
162      *
163      * @param input input stream
164      * @param offset offset
165      * @param length length
166      * @return data bytes
167      * @throws IOException read exception
168      */
readInputByOffsetAndLength(InputStream input, long offset, long length)169     public static byte[] readInputByOffsetAndLength(InputStream input, long offset, long length) throws IOException {
170         input.skip(offset);
171         return readInputByLength(input, length);
172     }
173 
174     /**
175      * Read byte from input stream.
176      *
177      * @param input InputStream
178      * @param length length
179      * @return data bytes
180      */
readInputByLength(InputStream input, long length)181     public static byte[] readInputByLength(InputStream input, long length) throws IOException {
182         try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
183             if (length > Integer.MAX_VALUE) {
184                 throw new IOException("Size cannot be greater than Integer max value: " + length);
185             }
186             writeInputToOutPut(input, output, length);
187             return output.toByteArray();
188         }
189     }
190 
191     /**
192      * write input to output by length
193      */
writeInputToOutPut(InputStream input, OutputStream output, long length)194     private static void writeInputToOutPut(InputStream input, OutputStream output, long length) throws IOException {
195         byte[] buffer = new byte[FILE_BUFFER_BLOCK];
196         long hasReadLen = 0L;
197         while (hasReadLen < length) {
198             int readLen = (int) Math.min(length - hasReadLen, FILE_BUFFER_BLOCK);
199             int len = input.read(buffer, 0, readLen);
200             if (len != readLen) {
201                 throw new IOException("read" + hasReadLen + "bytes data less than " + length);
202             }
203             output.write(buffer, 0, len);
204             hasReadLen += len;
205         }
206     }
207 
208     /**
209      * Out put content to file.
210      *
211      * @param content Which content to out put
212      * @param output  File to write
213      * @throws IOException Write failed
214      */
write(byte[] content, File output)215     public static void write(byte[] content, File output) throws IOException {
216         if (output.exists() && !output.canWrite()) {
217             CustomException.throwException(ERROR.WRITE_FILE_ERROR, SignToolErrMsg.FILE_WRITE_FAILED
218                     .toString("No permission write to file " + output));
219         }
220         try (FileOutputStream out = new FileOutputStream(output)) {
221             for (byte con : content) {
222                 out.write(con);
223             }
224         }
225     }
226 
227     /**
228      * Write data in file to output stream
229      *
230      * @param inFile input file path.
231      * @param out output file path.
232      * @param offset file read offset
233      * @param size file read size
234      * @return true, if write successfully.
235      */
appendWriteFileByOffsetToFile(String inFile, FileOutputStream out, long offset, long size)236     public static boolean appendWriteFileByOffsetToFile(String inFile, FileOutputStream out, long offset, long size) {
237         File inputFile = new File(inFile);
238         try (FileInputStream fis = new FileInputStream(inputFile)) {
239             fis.skip(offset);
240             writeInputToOutPut(fis, out, size);
241             return true;
242         } catch (FileNotFoundException e) {
243             LOGGER.error("Failed to get input stream object.");
244         } catch (IOException e) {
245             LOGGER.error("Failed to read or write data.");
246         }
247         return false;
248     }
249 
250     /**
251      * Check file exist or not.
252      *
253      * @param filePath File path
254      * @return Is file exist
255      */
isFileExist(String filePath)256     public static boolean isFileExist(String filePath) {
257         return new File(filePath).exists();
258     }
259 
260     /**
261      * Throw runtime exception if not allowed file type.
262      *
263      * @param filePath file path
264      * @param types    Such as "txt" "json" "mp3"
265      */
validFileType(String filePath, String... types)266     public static void validFileType(String filePath, String... types) {
267         String suffix = getSuffix(filePath);
268         ValidateUtils.throwIfNotMatches(!StringUtils.isEmpty(suffix),
269                 ERROR.NOT_SUPPORT_ERROR, SignToolErrMsg.NOT_SUPPORT_FILE.toString(filePath));
270         boolean isMatches = false;
271         for (String type : types) {
272             if (StringUtils.isEmpty(type)) {
273                 continue;
274             }
275             if (type.equalsIgnoreCase(suffix)) {
276                 isMatches = true;
277                 break;
278             }
279         }
280         ValidateUtils.throwIfNotMatches(isMatches,
281                 ERROR.NOT_SUPPORT_ERROR, SignToolErrMsg.NOT_SUPPORT_FILE.toString(filePath));
282     }
283 
284     /**
285      * Get suffix of file.
286      *
287      * @param filePath file path
288      * @return file suffix. Such as "txt" "json" "p12"
289      */
getSuffix(String filePath)290     public static String getSuffix(String filePath) {
291         if (StringUtils.isEmpty(filePath)) {
292             return "";
293         }
294         File file = new File(filePath);
295         String fileName = file.getName();
296         String[] temps = fileName.split("\\.");
297         if (temps.length < SPLIT_LENGTH) {
298             return "";
299         }
300         return temps[temps.length - 1];
301     }
302 
303     /**
304      * Write data in file to output stream
305      *
306      * @param file input file path.
307      * @param dos output stream.
308      * @return true, if write successfully.
309      */
writeFileToDos(String file, DataOutputStream dos)310     public static boolean writeFileToDos(String file, DataOutputStream dos) {
311         if (dos == null) {
312             return false;
313         }
314         File src = new File(file);
315         try (FileInputStream fileStream = new FileInputStream(src)) {
316             int temp;
317             byte[] buf = new byte[FILE_BUFFER_BLOCK];
318             while ((temp = fileStream.read(buf)) > 0) {
319                 dos.write(buf, 0, temp);
320             }
321             return true;
322         } catch (FileNotFoundException e) {
323             LOGGER.error("Failed to get input stream object.");
324         } catch (IOException e) {
325             LOGGER.error("Failed to read or write data.");
326         }
327         return false;
328     }
329 
330     /**
331      * Write byte array to output stream
332      *
333      * @param data byte array
334      * @param dos output stream
335      * @return true, if write successfully.
336      */
writeByteToDos(byte[] data, DataOutputStream dos)337     public static boolean writeByteToDos(byte[] data, DataOutputStream dos) {
338         if (data == null) {
339             return true;
340         }
341         try {
342             dos.write(data);
343         } catch (IOException e) {
344             LOGGER.error("Failed to write data to output stream.");
345             return false;
346         }
347         return true;
348     }
349 
350     /**
351      * Get length of file.
352      *
353      * @param filePath input file path.
354      * @return long value of file length.
355      */
getFileLen(String filePath)356     public static long getFileLen(String filePath) {
357         File file = new File(filePath);
358         if (file.exists() && file.isFile()) {
359             return file.length();
360         }
361         return -1;
362     }
363 
364     /**
365      * Write byte array data to output file.
366      *
367      * @param bytes byte array data.
368      * @param outFile output file path.
369      * @return true, if write successfully.
370      */
writeByteToOutFile(byte[] bytes, String outFile)371     public static boolean writeByteToOutFile(byte[] bytes, String outFile) {
372         try (OutputStream ops = new FileOutputStream(outFile, true)) {
373             return writeByteToOutFile(bytes, ops);
374         } catch (FileNotFoundException e) {
375             LOGGER.error("Failed to get output stream object, outfile: " + outFile);
376         } catch (IOException e) {
377             LOGGER.error("Failed to write data to ops, outfile: " + outFile);
378         }
379         return false;
380     }
381 
382     /**
383      * Write byte array data to output file.
384      *
385      * @param bytes byte array data.
386      * @param outFile output file path.
387      * @return true, if write successfully.
388      */
writeByteToOutFile(byte[] bytes, OutputStream outFile)389     public static boolean writeByteToOutFile(byte[] bytes, OutputStream outFile) {
390         try {
391             outFile.write(bytes, 0, bytes.length);
392             outFile.flush();
393             return true;
394         } catch (FileNotFoundException e) {
395             LOGGER.error("Failed to get output stream object, outfile: " + outFile);
396         } catch (IOException e) {
397             LOGGER.error("Failed to write data to ops, outfile: " + outFile);
398         }
399         return false;
400     }
401 
402     /**
403      * Check input file is valid.
404      *
405      * @param file input file.
406      * @throws IOException file is a directory or can't be read.
407      */
isValidFile(File file)408     public static void isValidFile(File file) throws IOException {
409         if (!file.exists()) {
410             throw new FileNotFoundException("File '" + file + "' does not exist");
411         }
412 
413         if (file.isDirectory()) {
414             throw new IOException("File '" + file + "' exists but is a directory");
415         }
416 
417         if (!file.canRead()) {
418             throw new IOException("File '" + file + "' cannot be read");
419         }
420     }
421 
422     /**
423      * Open an input stream of input file safely.
424      *
425      * @param file input file.
426      * @return an input stream of input file
427      * @throws IOException file is a directory or can't be read.
428      */
openInputStream(File file)429     public static FileInputStream openInputStream(File file) throws IOException {
430         isValidFile(file);
431         return new FileInputStream(file);
432     }
433 
toByteArray(final InputStream input, final int size)434     private static byte[] toByteArray(final InputStream input, final int size) throws IOException {
435         if (size < 0) {
436             throw new IllegalArgumentException("Size must be equal or greater than zero: " + size);
437         }
438 
439         if (size == 0) {
440             return EMPTY_BYTE_ARRAY;
441         }
442 
443         final byte[] data = new byte[size];
444         int offset = 0;
445         int read;
446 
447         while (offset < size && (read = input.read(data, offset, size - offset)) != FILE_END) {
448             offset += read;
449         }
450 
451         if (offset != size) {
452             throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size);
453         }
454 
455         return data;
456     }
457 
458     /**
459      * Read file to byte array.
460      *
461      * @param file input file.
462      * @return byte array of file-content.
463      * @throws IOException fill exception
464      */
readFileToByteArray(File file)465     public static byte[] readFileToByteArray(File file) throws IOException {
466         try (InputStream in = openInputStream(file)) {
467             final long fileLength = file.length();
468             if (fileLength > Integer.MAX_VALUE) {
469                 throw new IllegalArgumentException("Size cannot be greater than Integer max value: " + fileLength);
470             }
471             return toByteArray(in, (int) fileLength);
472         }
473     }
474 
475     /**
476      * Write a string to file
477      *
478      * @param source String will be written.
479      * @param filePath path to write file.
480      * @param charset a charset
481      * @throws IOException write error.
482      */
writeStringToFile(String source, String filePath, Charset charset)483     public static void writeStringToFile(String source, String filePath, Charset charset) throws IOException {
484         try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
485                 new FileOutputStream(filePath), charset))) {
486             writer.write(source);
487             writer.flush();
488         }
489     }
490 
491     /**
492      * Delete a file quietly
493      *
494      * @param file the file to delete
495      */
deleteFile(File file)496     public static void deleteFile(File file) {
497         if (file != null && file.isFile()) {
498             try {
499                 Files.delete(file.toPath());
500             } catch (IOException e) {
501                 LOGGER.warn("delete file '{}' error, error message: {}", file, e.getMessage());
502             }
503         }
504     }
505 
506     /**
507      * regex filename
508      *
509      * @param name filename
510      * @return boolean
511      */
isRunnableFile(String name)512     public static boolean isRunnableFile(String name) {
513         if (StringUtils.isEmpty(name)) {
514             return false;
515         }
516         if (name.endsWith(NATIVE_LIB_AN_SUFFIX) || name.endsWith(ABC_FILE_SUFFIX)) {
517             return true;
518         }
519         if (name.startsWith(LIBS_PATH_PREFIX)) {
520             return true;
521         }
522         return false;
523     }
524 }
525