Java 中如何检测 InputStream 中的内容

2023-11-26  本文已影响0人  雁过留声_泪落无痕

背景

我有一个 InputStream,同时有一个 save(InputStream) 方法将该流所代表的文件上传到文件服务器。

那么有没有什么办法既能读取原始 InputStream 中的数据进行检测,又能将原始文件上传到文件服务器呢?

方案1

一个可行的方案是文件中转,先将 MultipartFile 中的流存储到本地,然后本地文件就可以多次生成 InputStream 了。

  1. 存储到本地 local.data
  2. 读取 local.data 并检测其内部数据
  3. 再次用 local.data 生成 InputStream 传递给 save 方法

方案2

方案1肯定是可行的,问题就在于效率比较低,涉及到了好多次的 IO 操作,有没有不转存的方案呢?下面来实现一下:

public class TestStream {

    public static void main(String[] args) {
        System.out.println("Hello world!");

        CheckLineStream checkLineStream = null;
        BufferedOutputStream bos = null;
        boolean hasError = false;
        try {
            checkLineStream = new CheckLineStream(new FileInputStream("HasJs.data"), new LineValidator() {
                @Override
                public boolean validate(String line) {
                    return !line.contains("/S /JavaScript");
                }
            });

            // 模拟 save 方法,使用 CheckLineStream
            bos = new BufferedOutputStream(new FileOutputStream("HasJs-new.data"));
            byte[] buffer = new byte[1024];
            int len;
            while ((len = checkLineStream.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineInvalidException e) {
            e.printStackTrace();
            hasError = true;
        } finally {
            if (checkLineStream != null) {
                try {
                    checkLineStream.close();
                } catch (IOException ignore) {
                    // ignore
                }
            }

            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException ignore) {
                    // ignore
                }
            }
        }

        if (hasError) {
            new File("HasJs-new.data").delete();
        }
    }

    /**
     * catch LineValidaException to handle error.
     */
    public static class CheckLineStream extends InputStream {

        private final InputStream inputStream;
        private final LineValidator lineValidator;
        private StringBuffer sb = new StringBuffer();

        public CheckLineStream(InputStream inputStream, LineValidator lineValidator) {
            this.inputStream = inputStream;
            this.lineValidator = lineValidator;
        }

        @Override
        public int read() throws IOException {
            int b = inputStream.read();
            if (b == -1) {
                return b;
            }

            char c = (char) b;
            if (c == '\n') {
                String line = sb.toString();
                if (lineValidator != null && !lineValidator.validate(line)) {
                    throw new LineInvalidException("Invalid line: " + line);
                }
                sb = new StringBuffer();
            } else {
                sb.append(c);
            }

            return b;
        }

        @Override
        public void close() throws IOException {
            super.close();
            inputStream.close();
        }
    }

    public interface LineValidator {
        /**
         * Validate line, return true if valid, otherwise return false.
         */
        boolean validate(@NotNull String line);
    }

    public static class LineInvalidException extends RuntimeException {
        public LineInvalidException(String message) {
            super(message);
        }
    }

}

参考了 Java IO 流的装饰操作,内部持有原始的流,代理出一个新的流供 svae 使用。

中间如果读满一行则对内容进行检测,检测无问题则继续,有问题就抛出异常。

需要注意的一点就是,最后需要判断是否有异常发生,如果有的话就需要把上传到 save 的文件进行删除操作。

上一篇 下一篇

猜你喜欢

热点阅读