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