美文网首页
Java 中如何检测 InputStream 中的内容

Java 中如何检测 InputStream 中的内容

作者: 雁过留声_泪落无痕 | 来源:发表于2023-11-26 11:44 被阅读0次

背景

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

  • 该 InputStream 不能再次生成,比如在 Spring 中的 MultipartFile 中(用户上传上来的文件)
  • 我们知道 InputStream 也不能被再次读取
  • mark reset 的本质是全部读取,大文件肯定不行

那么有没有什么办法既能读取原始 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 的文件进行删除操作。

相关文章

网友评论

      本文标题:Java 中如何检测 InputStream 中的内容

      本文链接:https://www.haomeiwen.com/subject/vrsawdtx.html