Last active
May 18, 2026 01:47
-
-
Save asdf913/5999aa2b2cd5c73ffcc9eb1f8d6c6b53 to your computer and use it in GitHub Desktop.
Method to detect a byte array is a valid XLSX data or not
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <dependencies> | |
| <!--https://mvnrepository.com/artifact/org.apache.commons/commons-lang3--> | |
| <dependency> | |
| <groupId>org.apache.commons</groupId> | |
| <artifactId>commons-lang3</artifactId> | |
| <version>3.20.0</version> | |
| </dependency> | |
| <!--https://mvnrepository.com/artifact/org.zeroturnaround/zt-zip--> | |
| <dependency> | |
| <groupId>org.zeroturnaround</groupId> | |
| <artifactId>zt-zip</artifactId> | |
| <version>1.17</version> | |
| </dependency> | |
| </dependencies> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.io.ByteArrayInputStream; | |
| import java.io.IOException; | |
| import java.io.InputStream; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Member; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Modifier; | |
| import java.util.Base64; | |
| import java.util.Base64.Decoder; | |
| import java.util.Objects; | |
| import java.util.function.Predicate; | |
| import java.util.zip.ZipEntry; | |
| import java.util.zip.ZipInputStream; | |
| import javax.xml.parsers.DocumentBuilder; | |
| import javax.xml.parsers.DocumentBuilderFactory; | |
| import javax.xml.parsers.ParserConfigurationException; | |
| import org.apache.commons.lang3.function.FailableFunction; | |
| import org.w3c.dom.Document; | |
| import org.w3c.dom.Element; | |
| import org.w3c.dom.NamedNodeMap; | |
| import org.w3c.dom.Node; | |
| import org.w3c.dom.NodeList; | |
| import org.xml.sax.SAXException; | |
| import org.zeroturnaround.zip.ZipUtil; | |
| public class XlsxUtil { | |
| public static void main(final String[] args) throws IOException, SAXException, ParserConfigurationException { | |
| // | |
| System.out.println(isXlsx(null)); | |
| // | |
| System.out.println(isXlsx(new byte[] {})); | |
| // | |
| System.out.println(isXlsx(new byte[] { 0 })); | |
| // | |
| System.out.println(isXlsx(new byte[] { 0, 0 })); | |
| // | |
| final Decoder decoder = Base64.getDecoder(); | |
| // | |
| System.out.println(isXlsx(decoder != null ? decoder.decode( | |
| "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") | |
| : null)); | |
| // | |
| } | |
| public static boolean isXlsx(final byte[] bs) throws IOException, SAXException, ParserConfigurationException { | |
| // | |
| boolean contentTypeXmlFound = false; | |
| // | |
| try (final InputStream is = testAndApply(Objects::nonNull, bs, ByteArrayInputStream::new, null); | |
| final ZipInputStream zis = testAndApply(Objects::nonNull, is, ZipInputStream::new, null)) { | |
| // | |
| ZipEntry ze = null; | |
| // | |
| while ((ze = getNextEntry(zis)) != null) { | |
| // | |
| if (contentTypeXmlFound = Objects.equals("[Content_Types].xml", ze.getName())) { | |
| // | |
| break; | |
| // | |
| } // if | |
| // | |
| } // while | |
| // | |
| } // try | |
| // | |
| boolean isXlsx = false; | |
| // | |
| if (contentTypeXmlFound) { | |
| // | |
| try (final InputStream is = testAndApply(Objects::nonNull, bs, ByteArrayInputStream::new, null)) { | |
| // | |
| try (final InputStream bais = testAndApply(x -> x != null && x.length > 0, | |
| ZipUtil.unpackEntry(is, "[Content_Types].xml"), ByteArrayInputStream::new, null)) { | |
| // | |
| final NodeList childNodes = getChildNodes(getDocumentElement( | |
| bais != null ? parse(newDocumentBuilder(DocumentBuilderFactory.newDefaultInstance()), bais) | |
| : null)); | |
| // | |
| for (int i = 0; i < getLength(childNodes); i++) { | |
| // | |
| if (Objects.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", | |
| getTextContent(getNamedItem(getAttributes(item(childNodes, i)), "ContentType"))) | |
| && (isXlsx = true)) { | |
| // | |
| break; | |
| // | |
| } // if | |
| // | |
| } // for | |
| // | |
| } // try | |
| // | |
| } // try | |
| // | |
| } // if | |
| // | |
| return isXlsx; | |
| // | |
| } | |
| private static ZipEntry getNextEntry(final ZipInputStream instance) { | |
| // | |
| Object obj = null; | |
| // | |
| try { | |
| // | |
| final Method method = ZipInputStream.class.getDeclaredMethod("getNextEntry"); | |
| // | |
| obj = Boolean.logicalOr(isStatic(method), instance != null) ? invoke(method, instance) : null; | |
| // | |
| } catch (final NoSuchMethodException | IllegalAccessException e) { | |
| // | |
| throw new RuntimeException(e); | |
| // | |
| } catch (final InvocationTargetException e) { | |
| // | |
| final Throwable targetException = e.getTargetException(); | |
| // | |
| throw targetException instanceof RuntimeException re ? re : new RuntimeException(targetException); | |
| // | |
| } // try | |
| // | |
| return obj instanceof ZipEntry ze ? ze : null; | |
| // | |
| } | |
| private static <T, R, E extends Throwable> R testAndApply(final Predicate<T> predicate, final T value, | |
| final FailableFunction<T, R, E> functionTrue, final FailableFunction<T, R, E> functionFalse) throws E { | |
| return test(predicate, value) ? apply(functionTrue, value) : apply(functionFalse, value); | |
| } | |
| private static <T, R, E extends Throwable> R apply(final FailableFunction<T, R, E> instance, final T value) | |
| throws E { | |
| return instance != null ? instance.apply(value) : null; | |
| } | |
| private static <T> boolean test(final Predicate<T> instance, final T value) { | |
| return instance != null && instance.test(value); | |
| } | |
| private static boolean isStatic(final Member instance) { | |
| return instance != null && Modifier.isStatic(instance.getModifiers()); | |
| } | |
| private static Object invoke(final Method method, final Object instance, final Object... args) | |
| throws IllegalAccessException, InvocationTargetException { | |
| return method != null ? method.invoke(instance, args) : null; | |
| } | |
| private static DocumentBuilder newDocumentBuilder(final DocumentBuilderFactory instance) | |
| throws ParserConfigurationException { | |
| return instance != null ? instance.newDocumentBuilder() : null; | |
| } | |
| private static Document parse(final DocumentBuilder instance, final InputStream is) | |
| throws SAXException, IOException { | |
| return instance != null ? instance.parse(is) : null; | |
| } | |
| private static Element getDocumentElement(final Document instance) { | |
| return instance != null ? instance.getDocumentElement() : null; | |
| } | |
| private static NodeList getChildNodes(final Node instance) { | |
| return instance != null ? instance.getChildNodes() : null; | |
| } | |
| private static int getLength(final NodeList instance) { | |
| return instance != null ? instance.getLength() : 0; | |
| } | |
| private static Node item(final NodeList instance, final int index) { | |
| return instance != null ? instance.item(index) : null; | |
| } | |
| private static NamedNodeMap getAttributes(final Node instance) { | |
| return instance != null ? instance.getAttributes() : null; | |
| } | |
| private static Node getNamedItem(final NamedNodeMap instance, final String name) { | |
| return instance != null ? instance.getNamedItem(name) : null; | |
| } | |
| private static String getTextContent(final Node instance) { | |
| return instance != null ? instance.getTextContent() : null; | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://en.wikipedia.org/wiki/Open_Packaging_Conventions#Package,_parts,_and_relationships
