· 6 years ago · Aug 11, 2019, 11:36 PM
1package com.util;
2
3import javax.crypto.*;
4import javax.crypto.spec.SecretKeySpec;
5import java.io.*;
6import java.net.InetSocketAddress;
7import java.net.Socket;
8import java.nio.ByteBuffer;
9import java.nio.channels.FileChannel;
10import java.nio.charset.StandardCharsets;
11import java.nio.file.Files;
12import java.nio.file.Paths;
13import java.security.InvalidKeyException;
14import java.security.NoSuchAlgorithmException;
15import java.util.*;
16import java.util.stream.Stream;
17import java.util.zip.ZipEntry;
18import java.util.zip.ZipOutputStream;
19
20/**
21 * This class contains utilities for assisting in file-related things
22 * including the following:
23 * Conversion between byte[] and Byte[].
24 * To write Byte[] as a String and to get a String from a Byte[].
25 * To write and read String[] from/to files.
26 * To convert between HashMap<String, Object) and String[].
27 * To write and append to files.
28 * Clear a file and to remove all unnecisary \n and blanks.
29 * To encrypt and decrypt files using AES and a Secret Key.
30 * Convert between String and SecretKey.
31 * Generate SecretKey.
32 * Get public and local IP address for this system.
33 * String utilities to read the following from a file path: file name, extension, file size.
34 *
35 * @author Jacob Gordon
36 * @version 1.0
37 * @date 7/3/19
38 **/
39public class FileUtils {
40
41 /**
42 * Our Encryption/Decryption will use AES encryption/decryption.
43 */
44 public static final String CRYPT_ALGORITHM = "AES";
45
46 /**
47 * Used as the first parameter in AES Encryption/Decryption.
48 * ENCRYPT transforms a file of any kind given a secret-key.
49 */
50 public static final int ENCRYPT = Cipher.ENCRYPT_MODE;
51 public static final int DECRYPT = Cipher.DECRYPT_MODE;
52
53 /**
54 * Converts an Array of T objects to an ArrayList of T objects.
55 * @param array to convert.
56 * @param <T> object type of the list and resulting array list.
57 * @return array list containing all elements in array, or an empty list if null..
58 */
59 public static <T>ArrayList<T> arrayToArrayList(T[] array) {
60 ArrayList<T> list = new ArrayList<>();
61 if (array == null) return list;
62 if (array.length == 0) return list;
63 list.addAll(Arrays.asList(array));
64 return list;
65 }
66
67
68 /**
69 * Generates a Secret-Key using AES encryption & base 64.
70 * @return secret-key or null if failed.
71 */
72 public static SecretKey generateSecretKey() {
73 // create new key
74 SecretKey secretKey = null;
75 try {
76 return secretKey = KeyGenerator.getInstance("AES").generateKey();
77 } catch (NoSuchAlgorithmException e) {
78 e.printStackTrace();
79 }
80 return null;
81 }
82
83 /**
84 * Converts a SecretKey to a String using 64 bit coding.
85 * @param sk secret-key to convert to a String.
86 * @return a String representing the secret-key.
87 */
88 public static String keyToString(SecretKey sk) {
89 return Base64.getEncoder().encodeToString(sk.getEncoded());
90 }
91
92 /**
93 * Converts a String to a SecretKey object.
94 * @param string to convert to a SecretKey.
95 * @return a SecretKey constructed from the given String.
96 */
97 public static SecretKey stringToKey(String string) {
98 byte[] decodedKey = Base64.getDecoder().decode(string);
99 return new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
100 }
101
102 /**
103 * AES encryption/decryption based on a given input and output path.
104 * @param cipherMode ENCRYPT or DECRYPT, accordingly.
105 * @param secretKey unique key which determines the encryption/decryption.
106 * @param inputPath of the file to read data from.
107 * @param outputPath of the file to read the encrypted data to.
108 * @return true if successful encryption/decryption.
109 */
110 public static boolean EncryptDecrypt(int cipherMode, SecretKey secretKey, String inputPath, String outputPath){
111 try {
112 File inputFile, outputFile;
113 inputFile = new File(inputPath);
114 outputFile = new File(outputPath);
115 if (!doesFileExist(inputPath)) {
116 inputFile.createNewFile();
117 }
118 if (!doesFileExist(outputPath)) {
119 outputFile.getParentFile().mkdirs();
120 outputFile.createNewFile();
121 }
122 Cipher cipher = Cipher.getInstance(CRYPT_ALGORITHM);
123 cipher.init(cipherMode, secretKey);
124
125 FileInputStream inputStream = new FileInputStream(inputFile);
126 byte[] inputBytes = new byte[(int) inputFile.length()];
127 inputStream.read(inputBytes);
128
129 byte[] outputBytes = cipher.doFinal(inputBytes);
130
131 FileOutputStream outputStream = new FileOutputStream(outputFile);
132 outputStream.write(outputBytes);
133
134 inputStream.close();
135 outputStream.close();
136 return true;
137 } catch (NoSuchPaddingException | NoSuchAlgorithmException
138 | InvalidKeyException | BadPaddingException
139 | IllegalBlockSizeException | IOException e) {
140 e.printStackTrace();
141 }
142 return false;
143 }
144
145 /**
146 * Separator will not change at runtime.
147 */
148 private static String sep = null;
149
150 /**
151 * Gets the file seperator for the System. "/" for OSX & "\" for Windows.
152 * @return file separator for the current OS as a String.
153 */
154 public static String getSeparator() {
155 if (sep == null) sep = System.getProperty("file.separator");
156 return sep;
157 }
158
159 /**
160 * Reads the public IP address associated with this system.
161 * @author YCF_L
162 * @return IP address as a String.
163 * (https://stackoverflow.com/questions/9481865/getting-the-ip-address-of-the-current-machine-using-java)
164 */
165 public static String getPublicAddress() {
166 String ipAdressDns = "";
167 try {
168 String command = "nslookup myip.opendns.com resolver1.opendns.com";
169 Process proc = Runtime.getRuntime().exec(command);
170 BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
171 String s;
172 StringBuilder sb = new StringBuilder();
173 while ((s = stdInput.readLine()) != null) {
174 ipAdressDns += s + "\n";
175 }
176 String[] ss = ipAdressDns.split("\n");
177 ipAdressDns = ss[ss.length - 1];
178 ipAdressDns = ipAdressDns.substring(getIndexFirstOccurance(ipAdressDns, ":") + 2);
179 } catch (IOException e) {
180 return "[ERROR] CANT FIND PUBLIC ADDRESS";
181 }
182 return ipAdressDns;
183 }
184
185 /**
186 * Reads the local IP address associated with this system.
187 * @return IP address as a String, or null.
188 */
189 public static String getLocalAddress() {
190 Socket socket = new Socket();
191 try {
192 socket.connect(new InetSocketAddress("google.com", 80));
193 } catch (IOException e) {
194 return "[ERROR] CAN'T FIND LOCAL ADDRESS";
195 }
196 return socket.getLocalAddress().getHostAddress();
197 }
198
199 /**
200 * Reads the file as an array of Bytes.
201 * @param path absolute path of file on system.
202 * @return array of bytes representing the data.
203 */
204 public static byte[] readFileAsBytes(String path) {
205 if (!doesFileExist(path)) return null;
206 byte[] b = null;
207 try {
208 b = Files.readAllBytes(Paths.get(path));
209 } catch (IOException e) {
210 }
211 return b;
212 }
213
214 /**
215 * Writes to the file at the given path with the given data.
216 * @param path of the file we are writing to.
217 * @param bytes data that we want to write to the file.
218 */
219 public static void writeFileAsBytes(String path, byte[] bytes) {
220 try {
221 RandomAccessFile stream = new RandomAccessFile(path, "rw");
222 FileChannel channel = stream.getChannel();
223 ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
224 buffer.put(bytes);
225 buffer.flip();
226 channel.write(buffer);
227 stream.close();
228 channel.close();
229 } catch (IOException ex) {
230 ex.printStackTrace();
231 }
232 }
233
234 /**
235 * Clears all content and replaces it with an empty string, if the file exists.
236 * @param filePath
237 * @return
238 */
239 public static boolean clearFile(String filePath) {
240 if (!doesFileExist(filePath)) return false;
241 writeFileAsStrings(false, filePath, "");
242 return true;
243 }
244
245 /**
246 * Removes all blanks from a file.
247 * @param path
248 * @return
249 */
250 public static boolean removeBlanks(String path) {
251 if (!doesFileExist(path)) return false;
252 String[] strs = FileUtils.readFileAsStrings(path);
253 if (strs.length == 0) {
254 FileUtils.writeFileAsStrings(false, strs[0].replaceAll(getSeparator() + "\n", "").replace(" ", ""));
255 return true;
256 }
257 ArrayList<String> list = new ArrayList<>();
258 for (String s: strs) if (!s.replaceAll(getSeparator() + "\n", "").replace(" ", "").isEmpty()) list.add(s);
259 FileUtils.writeFileAsStrings(true, path, list.toArray(new String[0]));
260 return list.size() != strs.length;
261 }
262
263 /**
264 * Reads the given file as an array of Strings.
265 * @param filePath of the file to read.
266 * @return strings read line by line as an array, or null if no file found.
267 */
268 public static String[] readFileAsStrings(String filePath) {
269 if (!FileUtils.doesFileExist(filePath)) return null;
270 ArrayList<String> strings = new ArrayList<>();
271 try (Stream<String> stream = Files.lines( Paths.get(filePath), StandardCharsets.UTF_8)) {
272 stream.forEach(s -> strings.add(s));
273 }
274 catch (IOException e) {
275 e.printStackTrace();
276 }
277 return strings.toArray(new String[strings.size()]);
278 }
279
280 /**
281 * Reads the files as a single String.
282 * @param filePath to read.
283 * @return a String representing the file data.
284 */
285 public static String readFileAsString(String filePath) {
286 if (!doesFileExist(filePath)) return null;
287 StringBuilder sb = new StringBuilder();
288 try (Stream<String> stream = Files.lines(Paths.get(filePath), StandardCharsets.UTF_8)) {
289 stream.forEach(s -> sb.append(s));
290 }
291 catch (IOException e) {
292 e.printStackTrace();
293 }
294 return sb.toString();
295 }
296
297 /**
298 * Write the given strings in the file, not separated by anything.
299 * @param lineBreaks true if to include line breaks in each given line (if not included).
300 * @param path of file to write to. Will create if empty.
301 * @param data to write to the file.
302 */
303 public static void writeFileAsStrings(boolean lineBreaks, String path, String ... data) {
304 try {
305 FileWriter fw = new FileWriter(path);
306 for (String s: data) {
307 String str = s;
308 if (lineBreaks && !s.endsWith("\n") && !data[data.length - 1].equals(s)) str += "\n";
309 fw.write(str);
310 }
311 fw.close();
312 } catch (IOException e) {
313 e.printStackTrace();
314 }
315 }
316
317 /**
318 * Writes to the given strings in the file, after existing data.
319 * @param lineBreak true if to include linebreaks in both existing data and given data (if not included).
320 * @param path of file to write to. Will create if empty.
321 * @param data to write to after existing data.
322 */
323 public static void appendFileAsStrings(boolean lineBreak, String path, String ... data) {
324 if (doesFileExist(path)) {
325 String[] currentData = readFileAsStrings(path);
326 String[] d = new String[currentData.length + data.length];
327 int v = 0;
328 for (String s: currentData) {
329 if (lineBreak && !s.endsWith("\n")) s += "\n";
330 d[v++] = s;
331 }
332 for (String s: data) {
333 if (lineBreak && !s.endsWith("\n")) s += "\n";
334 d[v++] = s;
335 }
336 if (d[d.length - 1].endsWith("\n")) d[d.length - 1] = d[d.length - 1].substring(0, getIndexLastOccurance(d[d.length - 1], "\n"));
337 writeFileAsStrings(lineBreak, path, d);
338 } else {
339 writeFileAsStrings(lineBreak, path, data);
340 }
341 }
342
343 /**
344 * Writes to the given bytes in the file, after existing bytes.
345 * @param path of file to append to.
346 * @param bytes data to append (add to the current data).
347 */
348 public static void appendFileAsBytes(String path, byte[] bytes) {
349 if (doesFileExist(path)) {
350 byte[] data = readFileAsBytes(path);
351 byte[] d = new byte[data.length + bytes.length];
352 int v = 0;
353 for (byte b: data) {
354 d[v++] = b;
355 }
356 for (byte b: bytes) {
357 d[v++] = b;
358 }
359 writeFileAsBytes(path, d);
360 } else {
361 writeFileAsBytes(path, bytes);
362 }
363 }
364
365 /**
366 * Returns true if only if the file at the given path is empty.
367 * Returns false if the file doesn't exist or if it is not empty.
368 * @param path of the file to check
369 * @return true only if file at path is empty.
370 */
371 public static boolean isFileEmpty(String path) {
372 if (!doesFileExist(path)) return false;
373 String str = FileUtils.readFileAsString(path);
374 if (str == null) return false;
375 if (str.isEmpty()) return true;
376 String s = str.replaceAll("\n", "").replace(" ", "");
377 return s.isEmpty();
378 }
379
380 /**
381 * Given a HashMap of keys and values, return a String with the following syntax:
382 * 'key:value'. Use line boolean to add line breaks.
383 * @param map to convert keys (String before ':') and values (Objects after ':') to a String[].
384 * @param lineBreak true if '\n' to included after each line.
385 * @return an Array containing each key and value as a value in the array.
386 */
387 public static String[] convertKeyValueMap(HashMap<String, Object> map, boolean lineBreak) {
388 ArrayList<String> strings = new ArrayList<>();
389 for (String key: map.keySet()) {
390 Object value = map.get(key);
391 String str = key + ":" + value;
392 if (lineBreak && !str.endsWith("\n")) str += "\n";
393 strings.add(str);
394 }
395 return strings.toArray(new String[strings.size()]);
396 }
397
398 /**
399 * Supported values: String, int, double, float, boolean (true, false).
400 * @param lines of string data. Valid keys contain ':' after the identifier.
401 * @return a HashMap containing the keys (identifier) and values (following the ':').
402 */
403 public static HashMap<String, Object> readKeyValueMap(String[] lines) {
404 HashMap<String, Object> map = new HashMap<>();
405 for (String line: lines) {
406 if (!line.contains(":")) continue;
407 String key = line.substring(0, getIndexFirstOccurance(line, ":"));
408 String value = line.substring(getIndexFirstOccurance(line, ":") + 1);
409 //try int, double, float, else string.
410 try {
411 map.put(key, Integer.parseInt(value));
412 } catch (NumberFormatException ex) {
413 try {
414 map.put(key, Double.parseDouble(value));
415 } catch (NumberFormatException ex2) {
416 try {
417 map.put(key, Float.parseFloat(value));
418 } catch (NumberFormatException ex3) {
419 String v = value.toLowerCase();
420 if (v.equals("true") || v.equals("false")) {
421 map.put(key, Boolean.valueOf(value.toLowerCase()));
422 } else {
423 map.put(key, value);
424 }
425 }
426 }
427 }
428 }
429 return map;
430 }
431
432 /**
433 * Prints the given key-value-map to a String or Console.
434 * @param console true if to print to console.
435 * @param map to print.
436 * @return the printed string.
437 */
438 public static String printKeyValueMap(HashMap<String, Object> map, boolean console) {
439 StringBuilder sb = new StringBuilder();
440 for (String key: map.keySet()) {
441 sb.append(key).append(":").append(map.get(key).toString()).append("\n");
442 }
443 if (console) System.out.println(sb.toString());
444 return sb.toString();
445 }
446
447 /**
448 * Prints a given file line by line if applicable.
449 * @param path
450 * @param console
451 * @return
452 */
453 public static String printFile(String path, boolean console) {
454 if (!doesFileExist(path)) {
455 if (console) System.out.println("NULL");
456 return "NULL";
457 }
458 if (isFileEmpty(path)) {
459 if (console) System.out.println("**");
460 return "**";
461 }
462 StringBuilder sb = new StringBuilder();
463 String[] strs = FileUtils.readFileAsStrings(path);
464 int k = 0;
465 for (String str: strs) {
466 sb.append("*").append(str).append("*");
467 if (k++ != strs.length && !str.endsWith("\n")) sb.append("\n");
468 }
469 if (console) System.out.println(sb.toString());
470 return sb.toString();
471 }
472
473 /**
474 * String to seperate each byte; should be unique and should not be representative
475 * of a Byte.
476 */
477 private static final String byteSep = "^";
478
479 /**
480 * Reads an array of Bytes as a single String.
481 * Bytes are seperated by 'byteSep' and are integers.
482 * @param bytes
483 * @return
484 */
485 public static String byteArrayToString(Byte[] bytes) {
486 StringBuilder sb = new StringBuilder();
487 for (Byte b: bytes) {
488 sb.append(b).append(byteSep);
489 }
490 return sb.toString();
491 }
492
493 /**
494 * Reads a String as a Byte[] separated by 'byteSep'
495 * Returns null if of format error.
496 * @param str
497 * @return
498 */
499 public static Byte[] stringToByteArray(String str) {
500 String[] strings = str.split(byteSep);
501 Byte[] bytes = new Byte[strings.length];
502 for (int i = 0; i < strings.length; i++) {
503 bytes[i] = Byte.valueOf(strings[i]);
504 }
505 return bytes;
506 }
507
508 /**
509 * Converts the given Byte[] to a byte[].
510 * @param data data of type Byte[] to convert.
511 * @return data of type byte[].
512 */
513 public static byte[] convertBytes(Byte[] data) {
514 byte[] b = new byte[data.length];
515 int i = 0;
516 for (Byte bb: data) {
517 b[i++] = bb;
518 }
519 return b;
520 }
521
522 /**
523 * Converts the given byte[] to a Byte[].
524 * @param data data of type byte[].
525 * @return data of type Byte[].
526 */
527 public static Byte[] convertBytes(byte[] data) {
528 Byte[] b = new Byte[data.length];
529 int i = 0;
530 for (byte bb: data) {
531 b[i++] = bb;
532 }
533 return b;
534 }
535
536 /**
537 * Directory will not change at runtime.
538 */
539 private static String dd;
540
541 /**
542 * Gets the path of the user's desktop directory.
543 * @return path as a string within the directory.
544 */
545 public static String getDesktopDirectory() {
546 if (dd == null) dd = getHomeDirectory() + "desktop" + getSeparator();
547 return dd;
548 }
549
550
551 /**
552 * Directory will not change at runtime.
553 */
554 private static String hd;
555
556 /**
557 * Gets the path of the user's home directory.
558 * @return path as a string within the directory.
559 */
560 public static String getHomeDirectory() {
561 if (hd == null) hd = System.getProperty("user.home") + getSeparator();
562 return hd;
563 }
564
565 /**
566 * Returns true if this directory exists.
567 * @param path of the directory.
568 * @return true if exists and is a directory. False otherwise.
569 */
570 public static boolean doesDirectoryExist(String path) {
571 File f = new File(path);
572 return f.isDirectory();
573 }
574
575 /**
576 * Returns true if this file exists.
577 * @param path of the file.
578 * @return true if exists and is a file. False otherwise.
579 */
580 public static boolean doesFileExist(String path) {
581 File f = new File(path);
582 return f.isFile();
583 }
584
585 /**
586 * Creates a directory and returns true if successful.
587 * @param path of the directory to create, if not occupied by a file or directory.
588 * @return true if a directory was created. False if otherwise.
589 */
590 public static boolean createDir(String path) {
591 if (doesDirectoryExist(path) || doesFileExist(path)) return false;
592 return new File(path).mkdirs();
593 }
594
595 /**
596 * Creates a file and returns true if successful.
597 * @param path of the file to create, if not occupied by a file or directory.
598 * @return true if a file was created. False if otherwise.
599 */
600 public static boolean createFile(String path) {
601 if (doesDirectoryExist(path) || doesFileExist(path)) return false;
602 try {
603 return new File(path).createNewFile();
604 } catch (IOException e) {
605 e.printStackTrace();
606 }
607 return false;
608 }
609
610 /**
611 * Gets the extension of a given file path as a String.
612 * @param path of the file to read just the extension.
613 * @return the string after "." if it exists.
614 */
615 public static String getFileExtension(String path) {
616 if (!path.contains(".") || path.endsWith(".")) return "";
617 return path.substring(getIndexLastOccurance(path, ".") + 1);
618 }
619
620 /**
621 * Gets the file name given a full path.
622 * @param path of the file.
623 * @return string after the last "/ or \" to the last ".".
624 */
625 public static String getFileName(String path) {
626 if (!path.contains(getSeparator()) || path.endsWith(getSeparator())) return path;
627 return path.substring(getIndexLastOccurance(path, getSeparator()) + 1);
628 }
629
630 /**
631 * Gets the file name given a full path.
632 * @param path of the file.
633 * @return string after the last "/ or \" to the last ".".
634 */
635 public static String getFileNameNoExt(String path) {
636 if (!path.contains(getSeparator()) || path.endsWith(getSeparator())) return path;
637 return path.substring(getIndexLastOccurance(path, getSeparator()) + 1, getIndexLastOccurance(path, "."));
638 }
639
640 /**
641 * Amount of bytes that fit in a MB.
642 */
643 private static final double BYTES_IN_MB = 1000000.0;
644
645 /**
646 * Amount of megabytes in gigabytes.
647 */
648 private static final double MB_IN_GB = 1000.0;
649
650 /**
651 * Amount of bytes in gigabytes.
652 */
653 private static final double BYTES_IN_GB = BYTES_IN_MB * MB_IN_GB;
654
655 /**
656 * Number of digits after "." to estimate the file size upto.
657 */
658 private static final int NUM_DIGITS_ESTIMATE = 2;
659
660 /**
661 * Returns a String containing the file size, following by
662 * B for bytes or ~MB for megabytes (estimated upto num_digits).
663 * @param path of the file.
664 * @return the size of the file as a String.
665 */
666 public static String getFileSize(String path) {
667 if (!doesFileExist(path)) return "";
668 StringBuilder sb = new StringBuilder();
669 File f = new File(path);
670 long l = f.length();
671 if (l < BYTES_IN_MB) return sb.append(l).append("B").toString();
672 String size = l < (BYTES_IN_GB) ? (l / BYTES_IN_MB) + "" : (l / (BYTES_IN_GB)) + "";
673 boolean isWhole = !size.contains(".");
674 if (!isWhole) {
675 int k = getIndexLastOccurance(size, ".");
676 size = size.substring(0, k) + size.substring(k).substring(0, NUM_DIGITS_ESTIMATE + 1);
677 }
678 return sb.append(isWhole ? "" : sb.append("~")).append(size).append((l < (BYTES_IN_GB) ? "MB" : "GB")).toString();
679 }
680
681 /**
682 * Returns a string with the sequence after the last occurrence 'v' in 'str'.
683 * 'str' must not end with 'v'.
684 * @param str to substring and check its contents.
685 * @param v return the sequence after this value is found'.
686 * @return
687 */
688 public static String getStringAfter(String str, String v) {
689 if (!str.contains(v) || v.length() == str.length() || str.endsWith(v)) return str;
690 return str.substring(getIndexLastOccurance(str, v) + 1);
691 }
692
693 /**
694 * Returns the index of the first occurance of 'v' in 'str'.
695 * @param str to substring and check its contents.
696 * @param v value to check what the string contains.
697 * @return the starting index of the first occurance of 'v' in 'str'. -1 if nothing found.
698 */
699 public static int getIndexFirstOccurance(String str, String v) {
700 for (int i = 0; i < str.length(); i++) {
701 if (i + v.length() <= str.length()) {
702 if (str.substring(i, i + v.length()).equals(v)) {
703 return i;
704 }
705 }
706 }
707 return -1;
708 }
709
710 /**
711 * Returns the index of the last occurance of 'v' in 'str'.
712 * @param str to substring and check its contents.
713 * @param v value to check what the string contains.
714 * @return the starting index of the first occurance of 'v' in 'str'. -1 if nothing found.
715 */
716 public static int getIndexLastOccurance(String str, String v) {
717 for (int i = str.length() - 1; i >= 0; i--) {
718 if (i + v.length() <= str.length()) {
719 if (str.substring(i, i + v.length()).equals(v)) {
720 return i;
721 }
722 }
723 }
724 return -1;
725 }
726}