From 0a2ad6130c075b62eab8594d29a86eddd9f765a8 Mon Sep 17 00:00:00 2001 From: Jim Procter Date: Tue, 28 Jul 2020 11:29:20 +0100 Subject: [PATCH] JAL-2656 recognise Gzipped file input streams by their content, not their filename --- src/jalview/io/FileParse.java | 70 +++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/jalview/io/FileParse.java b/src/jalview/io/FileParse.java index 167314e..128672a 100755 --- a/src/jalview/io/FileParse.java +++ b/src/jalview/io/FileParse.java @@ -26,10 +26,10 @@ import jalview.api.AlignmentViewPanel; import jalview.api.FeatureSettingsModelI; import jalview.util.MessageManager; +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -38,6 +38,7 @@ import java.io.StringReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLConnection; import java.util.zip.GZIPInputStream; @@ -185,29 +186,64 @@ public class FileParse } if (!error) { - if (fileStr.toLowerCase().endsWith(".gz")) + try { - try - { - dataIn = getGzipReader(new FileInputStream(fileStr)); - dataName = fileStr; - return error; - } catch (Exception x) - { - warningMessage = "Failed to resolve as a GZ stream (" - + x.getMessage() + ")"; - // x.printStackTrace(); - } - ; + dataIn = checkForGzipStream(new FileInputStream(fileStr)); + dataName = fileStr; + } catch (Exception x) + { + warningMessage = "Failed to resolve " + fileStr + + " as a data source. (" + x.getMessage() + ")"; + // x.printStackTrace(); + error = true; } - - dataIn = new BufferedReader(new FileReader(fileStr)); - dataName = fileStr; + ; } return error; } + + /** + * Recognise the 2-byte magic header for gzip streams + * + * https://recalll.co/ask/v/topic/java-How-to-check-if-InputStream-is-Gzipped/555aadd62bd27354438b90f6 + * + * @param bytes - at least two bytes + * @return + */ + private static boolean isGzipStream(byte[] bytes) { + int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + return (GZIPInputStream.GZIP_MAGIC == head); + } /** + * Returns a Reader for the given input after wrapping it in a buffered input + * stream, and then checking if it needs to be wrapped by a GZipInputStream + * + * @param input + * @return + */ + private BufferedReader checkForGzipStream(InputStream input) throws Exception { + + // NB: stackoverflow https://stackoverflow.com/questions/4818468/how-to-check-if-inputstream-is-gzipped + // could use a PushBackInputStream rather than a BufferedInputStream + + BufferedInputStream bufinput; + if (!input.markSupported()) { + bufinput= new BufferedInputStream(input,16); + input = bufinput; + } + input.mark(4); + byte[] bytes=input.readNBytes(2); + input.reset(); + if (bytes.length==2 && isGzipStream(bytes)) { + return getGzipReader(input); + } + // return a buffered reader for the stream. + InputStreamReader isReader= new InputStreamReader(input); + BufferedReader toReadFrom=new BufferedReader(isReader); + return toReadFrom; + } + /** * Returns a {@code BufferedReader} which wraps the input stream with a * GZIPInputStream. Throws a {@code ZipException} if a GZIP format error * occurs or the compression method used is unsupported. -- 1.7.10.2