|
| 1 | +package com.checkpoint.mgmt_api.utils; |
| 2 | + |
| 3 | +import java.nio.charset.StandardCharsets; |
| 4 | +import java.security.MessageDigest; |
| 5 | +import java.security.NoSuchAlgorithmException; |
| 6 | + |
| 7 | +/** |
| 8 | + * Utility class for filename sanitization to prevent "File name too long" filesystem errors. |
| 9 | + * Provides cross-platform compatible filename handling with length limits and character filtering. |
| 10 | + */ |
| 11 | +public class FileNameUtils { |
| 12 | + |
| 13 | + // Maximum filename length (excluding path and extension) to ensure compatibility across filesystems |
| 14 | + // Linux supports up to 255, Windows up to 260, but we use a conservative limit for safety |
| 15 | + private static final int MAX_FILENAME_LENGTH = 200; |
| 16 | + // Maximum length for the truncated portion before adding hash |
| 17 | + private static final int MAX_TRUNCATED_LENGTH = 150; |
| 18 | + |
| 19 | + /** |
| 20 | + * Sanitizes a filename to ensure it doesn't exceed filesystem limits. |
| 21 | + * If the filename is too long, it truncates the name and adds a hash suffix for uniqueness. |
| 22 | + * |
| 23 | + * This method handles cross-platform filename restrictions: |
| 24 | + * - Removes/replaces invalid characters for both Windows and Linux |
| 25 | + * - Enforces length limits compatible with most filesystems |
| 26 | + * |
| 27 | + * @param baseFileName the original filename (without extension) |
| 28 | + * @return sanitized filename that fits within filesystem limits |
| 29 | + */ |
| 30 | + public static String sanitizeFileName(String baseFileName) { |
| 31 | + if (baseFileName == null || baseFileName.isEmpty()) { |
| 32 | + return "unnamed"; |
| 33 | + } |
| 34 | + |
| 35 | + // Remove or replace invalid filename characters for cross-platform compatibility |
| 36 | + // Linux is more permissive, but we sanitize for Windows compatibility as well |
| 37 | + String sanitized = baseFileName |
| 38 | + .replaceAll("[<>:\"/\\\\|?*]", "_") // Windows invalid chars |
| 39 | + .replaceAll("\\s+", "_") // Replace multiple spaces with single underscore |
| 40 | + .replaceAll("_{2,}", "_") // Replace multiple underscores with single |
| 41 | + .replaceAll("^[._]+", "") // Remove leading dots/underscores (Linux hidden files) |
| 42 | + .replaceAll("[._]+$", ""); // Remove trailing dots/underscores |
| 43 | + |
| 44 | + // Ensure we have a valid filename after sanitization |
| 45 | + if (sanitized.isEmpty()) { |
| 46 | + sanitized = "unnamed"; |
| 47 | + } |
| 48 | + |
| 49 | + // Check if filename length is within limits |
| 50 | + if (sanitized.length() <= MAX_FILENAME_LENGTH) { |
| 51 | + return sanitized; |
| 52 | + } |
| 53 | + // Filename is too long, need to truncate and add hash for uniqueness |
| 54 | + String truncated = sanitized.substring(0, Math.min(sanitized.length(), MAX_TRUNCATED_LENGTH)); |
| 55 | + String hash = generateShortHash(baseFileName); // Use original name for hash to maintain uniqueness |
| 56 | + String result = truncated + "_" + hash; // Log the truncation for debugging purposes |
| 57 | + System.out.println("WARNING: Filename too long, truncated from '" + baseFileName + "' to '" + result + "'"); |
| 58 | + |
| 59 | + return result; |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * Generates a short hash from the input string to ensure filename uniqueness. |
| 64 | + * |
| 65 | + * @param input the string to hash |
| 66 | + * @return a short hash string (8 characters) |
| 67 | + */ |
| 68 | + public static String generateShortHash(String input) { |
| 69 | + try { |
| 70 | + MessageDigest md = MessageDigest.getInstance("MD5"); |
| 71 | + byte[] hash = md.digest(input.getBytes(StandardCharsets.UTF_8)); |
| 72 | + |
| 73 | + // Convert to hex and take first 8 characters for a short unique identifier |
| 74 | + StringBuilder sb = new StringBuilder(); |
| 75 | + for (int i = 0; i < Math.min(4, hash.length); i++) { |
| 76 | + sb.append(String.format("%02x", hash[i])); |
| 77 | + } |
| 78 | + return sb.toString(); |
| 79 | + } catch (NoSuchAlgorithmException e) { |
| 80 | + // Fallback: use hashCode if MD5 is not available |
| 81 | + return String.format("%08x", Math.abs(input.hashCode())); |
| 82 | + } |
| 83 | + } |
| 84 | +} |
0 commit comments