我的建议是:不要创建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() {
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所指出的那样,建议覆盖某些方法,例如close
和available
。如果您不实现它们,流将始终报告可供读取的字节数为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);
}
}
标记和重置
如果您想支持标记/重置,您可能还需要覆盖mark
、markSupported
和reset
方法。虽然我不确定容器是否会实际调用它们。
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;
}
}
@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) {
markedPosition = -1;
readLimit = -1;
}
}
return i;
} else {
return -1;
}
}
ServletInputStream
是从哪个包中来的?在 Java8 中好像被删除了。 - romfret