输入流转换为Servlet输入流

17

我有这个InputStream:

InputStream inputStream = new ByteArrayInputStream(myString.getBytes(StandardCharsets.UTF_8));

如何将这个转换为ServletInputStream?

我已经尝试过:

ServletInputStream  servletInputStream = (ServletInputStream) inputStream;

但是不起作用。

编辑:

我的方法是这样的:

private static class LowerCaseRequest extends HttpServletRequestWrapper {

        public LowerCaseRequest(final HttpServletRequest request) throws IOException, ServletException {
            super(request);
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {

            ServletInputStream servletInputStream;

            StringBuilder jb = new StringBuilder();
            String line;
            String toLowerCase = "";

            BufferedReader reader = new BufferedReader(new InputStreamReader(super.getInputStream()));
            while ((line = reader.readLine()) != null) {
                toLowerCase = jb.append(line).toString().toLowerCase();
            }

            InputStream inputStream = new ByteArrayInputStream(toLowerCase.getBytes(StandardCharsets.UTF_8));

            servletInputStream = (ServletInputStream) inputStream;

            return servletInputStream;

        }
 }

我正在尝试将所有请求转换为小写字母。


ServletInputStream 是从哪个包中来的?在 Java8 中好像被删除了。 - romfret
@romfret 它是Java Servlet API的一部分,而不是标准Java平台的一部分。Servlet API有一个不同的版本控制方案,例如在撰写本文时最新的版本是3.1。因此,您使用哪个Java版本(例如5、6、7、8)并不太重要,而更重要的是您使用哪个Servlet API版本(以及您的容器支持哪个版本)。 - Adam Burley
3个回答

38

我的建议是:不要创建ByteArrayInputStream,直接使用从getBytes方法获取的字节数组。这应该足以创建一个ServletInputStream

最基本的解决方案

不幸的是,aksappy的答案只覆盖了read方法。虽然在Servlet API 3.0及以下版本中可能已经足够,但在较新的Servlet API版本中,您还必须实现另外三个方法。

这是我的类实现,尽管由于Servlet API 3.1引入的新方法使得其变得相当冗长,您可能想考虑将其拆分为嵌套类甚至是顶级类。

    final byte[] myBytes = myString.getBytes("UTF-8");
    ServletInputStream servletInputStream = new ServletInputStream() {
        private int lastIndexRetrieved = -1;
        private ReadListener readListener = null;

        @Override
        public boolean isFinished() {
            return (lastIndexRetrieved == myBytes.length-1);
        }

        @Override
        public boolean isReady() {
            // This implementation will never block
            // We also never need to call the readListener from this method, as this method will never return false
            return isFinished();
        }

        @Override
        public void setReadListener(ReadListener readListener) {
            this.readListener = readListener;
            if (!isFinished()) {
                try {
                    readListener.onDataAvailable();
                } catch (IOException e) {
                    readListener.onError(e);
                }
            } else {
                try {
                    readListener.onAllDataRead();
                } catch (IOException e) {
                    readListener.onError(e);
                }
            }
        }

        @Override
        public int read() throws IOException {
            int i;
            if (!isFinished()) {
                i = myBytes[lastIndexRetrieved+1];
                lastIndexRetrieved++;
                if (isFinished() && (readListener != null)) {
                    try {
                        readListener.onAllDataRead();
                    } catch (IOException ex) {
                        readListener.onError(ex);
                        throw ex;
                    }
                }
                return i;
            } else {
                return -1;
            }
        }
    };

添加预期方法

根据您的要求,您可能还想覆盖其他方法。正如romfret所指出的那样,建议覆盖某些方法,例如closeavailable。如果您不实现它们,流将始终报告可供读取的字节数为0,并且close方法不会对流的状态产生任何影响。您可能可以没有覆盖skip,因为默认实现只会调用read多次。

    @Override
    public int available() throws IOException {
        return (myBytes.length-lastIndexRetrieved-1);
    }

    @Override
    public void close() throws IOException {
        lastIndexRetrieved = myBytes.length-1;
    }

编写更好的close方法

由于匿名类的特性,编写一个有效的close方法可能会很困难,因为只要 Java 没有垃圾回收一个流的实例,即使该流已关闭,它仍然会保留对字节数组的引用。

但是,如果你将该类分解为一个嵌套类或顶级类(甚至是具有构造函数的匿名类,可以从定义该类的行中调用),myBytes可以成为一个非 final 字段而不是 final 局部变量,然后你可以添加一行代码:

myBytes = null;

将代码添加到您的close方法中,这将允许Java释放由字节数组占用的内存。

当然,这将需要您编写一个构造函数,例如:

    private byte[] myBytes;

    public StringServletInputStream(String str) {
        try {
            myBytes = str.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("JVM did not support UTF-8", e);
        }
    }

标记和重置

如果您想支持标记/重置,您可能还需要覆盖markmarkSupportedreset方法。虽然我不确定容器是否会实际调用它们。

    private int readLimit = -1;
    private int markedPosition = -1;

    @Override
    public boolean markSupported() {
        return true;
    }

    @Override
    public synchronized void mark(int readLimit) {
        this.readLimit = readLimit;
        this.markedPosition = lastIndexRetrieved;
    }

    @Override
    public synchronized void reset() throws IOException {
        if (markedPosition == -1) {
            throw new IOException("No mark found");
        } else {
            lastIndexRetrieved = markedPosition;
            readLimit = -1;
        }
    }

    // Replacement of earlier read method to cope with readLimit
    @Override
    public int read() throws IOException {
        int i;
        if (!isFinished()) {
            i = myBytes[lastIndexRetrieved+1];
            lastIndexRetrieved++;
            if (isFinished() && (readListener != null)) {
                try {
                    readListener.onAllDataRead();
                } catch (IOException ex) {
                    readListener.onError(ex);
                    throw ex;
                }
                readLimit = -1;
            }
            if (readLimit != -1) {
                if ((lastIndexRetrieved - markedPosition) > readLimit) {
                    // This part is actually not necessary in our implementation
                    // as we are not storing any data. However we need to respect
                    // the contract.
                    markedPosition = -1;
                    readLimit = -1;
                }
            }
            return i;
        } else {
            return -1;
        }
    }

13

试试这段代码。

ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(myString.getBytes(StandardCharsets.UTF_8));
    ServletInputStream servletInputStream=new ServletInputStream(){
        public int read() throws IOException {
          return byteArrayInputStream.read();
        }
      }

2
ServletInputStream不是抽象的吗? - CaffeineToCode
8
如果您覆盖此方法,您应该覆盖其他方法(close等)。 - romfret
它是抽象的,但这个答案说明了通过使用匿名内部类实现抽象方法来实例化抽象类,所以没问题。然而,我们不应该创建自己的 ServletInputStream 实例,我们应该按照我的答案从 ServletRequest 中获取它们。 - NickJ
哇喔,太棒了 :) 感谢 aksappy - Chetan S. Choudhary

-3

你只能像这样转换:

ServletInputStream  servletInputStream = (ServletInputStream) inputStream;

如果您尝试转换的inputStream实际上已经是ServletInputStream,则可以进行转换。如果它是InputStream的其他实现,它将会报错。您不能将一个对象强制转换为它不是的类型。

在Servlet容器中,您可以从ServletRequest获取ServletInputStream:

ServletInputStream  servletInputStream = request.getInputStream();

那么,你实际上想做什么?

编辑

我很好奇为什么你想将请求转换为小写 - 为什么不只是使你的servlet不区分大小写呢?换句话说,你可以将代码复制到你的servlet中,然后在那里处理它...总是寻找最简单的解决方案!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接