IOUtils.toString(InputStream) 的番石榴等价物
Posted
技术标签:
【中文标题】IOUtils.toString(InputStream) 的番石榴等价物【英文标题】:Guava equivalent for IOUtils.toString(InputStream) 【发布时间】:2011-05-10 07:08:19 【问题描述】:Apache Commons IO 有一个很好的便捷方法 IOUtils.toString() 可以将 InputStream
读取到字符串。
由于我试图从 Apache Commons 转移到 Guava:在 Guava 中是否有等价物?我查看了com.google.common.io
包中的所有类,但找不到任何简单的东西。
编辑:我理解并理解字符集的问题。碰巧我知道我所有的来源都是 ASCII(是的,ASCII,不是 ANSI 等),所以在这种情况下,编码对我来说不是问题。
【问题讨论】:
关于字符集:对于一个库来说,要求你指定你知道你正在处理的字符集(例如Charsets.US_ASCII
)而不是让你说“嗯,无论我使用什么字符集,这仍然很好猜测?”很多人似乎都乐于这样做。特别是因为 Java 不使用有意义的默认值,例如 UTF-8。
我知道。这就是为什么我在自己的答案中使用 UTF-8 作为默认版本。
另见文档:code.google.com/p/guava-libraries/wiki/IOExplained
@Vadzim 提出这个问题时这些文档不存在:-)
【参考方案1】:
如果您有Readable
,则可以使用CharStreams.toString(Readable)
。因此,您可能可以执行以下操作:
String string = CharStreams.toString( new InputStreamReader( inputStream, "UTF-8" ) );
强制你指定一个字符集,我猜你应该这样做。
【讨论】:
实际上,我将结合使用您和 Jon Skeet 的答案:` CharStreams.toString(new InputStreamReader(supplier.get(), Charsets.UTF_8))` 是的,有很多组合选项的方法! @S.P.Floyd:如果您有InputSupplier<InputStream>
,我强烈建议您使用CharStreams.newReaderSupplier(supplier, Charsets.UTF_8)
而不是new InputStreamReader
。原因是当给定InputStreamReader
时,toString
将不会关闭Reader
(因此不会关闭底层流!)。通过将InputSupplier
用于Reader
,toString
方法将为您处理关闭Reader
。【参考方案2】:
几乎。你可以使用这样的东西:
InputSupplier<InputStreamReader> readerSupplier = CharStreams.newReaderSupplier
(streamSupplier, Charsets.UTF_8);
String text = CharStreams.toString(readerSupplier);
我个人不认为IOUtils.toString(InputStream)
是“不错的” - 因为它总是使用平台的默认编码,这几乎不是你想要的。有一个采用编码名称的重载,但使用名称不是一个好主意IMO。这就是我喜欢Charsets.*
的原因。
编辑:并不是说上面需要InputSupplier<InputStream>
作为streamSupplier
。如果你已经有了流,你可以很容易地实现它:
InputSupplier<InputStream> supplier = new InputSupplier<InputStream>()
@Override public InputStream getInput()
return stream;
;
【讨论】:
乔恩,是通过 request.getInputStream 流式传输的吗?另外,你会像@Calum 的回答中提到的 ColinD 那样关闭流吗? 哦,它是一个 servlet doPost 环境,我应该关闭流吗? @Blankman:啊,这就是你的背景——你的问题根本不清楚。是否关闭请求流并不重要,但我通常会这样做。不过我会编辑这个答案 - 似乎没有这样的超载。 我现在才这样做:String payLoad = CharStreams.toString(new InputStreamReader(request.getInputStream(), "UTF-8")); @BeeOnRope:我想一种中间方法是Charsets.UTF_8.name()
- 更耐打字。【参考方案3】:
您在对 Calum 的回答的评论中声明您将使用
CharStreams.toString(new InputStreamReader(supplier.get(), Charsets.UTF_8))
此代码有问题,因为重载CharStreams.toString(Readable)
状态:
不关闭
Readable
。
这意味着您的InputStreamReader
以及supplier.get()
返回的扩展InputStream
在此代码完成后不会关闭。
另一方面,如果您利用了您似乎已经拥有InputSupplier<InputStream>
并使用了重载CharStreams.toString(InputSupplier<R extends Readable & Closeable>
),则toString
方法将处理@ 的创建和关闭987654331@给你。
这正是 Jon Skeet 所建议的,除了实际上没有任何将 InputStream
用作输入的 CharStreams.newReaderSupplier
重载...你必须给它一个 InputSupplier
:
InputSupplier<? extends InputStream> supplier = ...
InputSupplier<InputStreamReader> readerSupplier =
CharStreams.newReaderSupplier(supplier, Charsets.UTF_8);
// InputStream and Reader are both created and closed in this single call
String text = CharStreams.toString(readerSupplier);
InputSupplier
的目的是让 Guava 处理需要丑陋的 try-finally
块以确保正确关闭资源的部分,从而使您的生活更轻松。
编辑:就个人而言,我发现以下内容(我实际上是这样写的,只是分解了上面代码中的步骤)
String text = CharStreams.toString(
CharStreams.newReaderSupplier(supplier, Charsets.UTF_8));
远比这更简洁:
String text;
InputStreamReader reader = new InputStreamReader(supplier.get(),
Charsets.UTF_8);
boolean threw = true;
try
text = CharStreams.toString(reader);
threw = false;
finally
Closeables.close(reader, threw);
这或多或少是您自己正确处理此问题所必须编写的内容。
编辑:2014 年 2 月
InputSupplier
和 OutputSupplier
以及使用它们的方法在 Guava 16.0 中已被弃用。他们的替代品是ByteSource
、CharSource
、ByteSink
和CharSink
。给定一个ByteSource
,您现在可以像这样以String
的形式获取其内容:
ByteSource source = ...
String text = source.asCharSource(Charsets.UTF_8).read();
【讨论】:
感谢您提供的重要信息 (+1)。但这非常冗长。我认为将接受的答案与 Closeables.closeQuietly() 结合起来更容易。 @CollinD:我在其中一个答案中使用了您的方法。请查看code 并告诉我这是否是使用 InputSupplier 的正确方法。 @ColinD,如果 inputStream 来自 doPost servlet 内部,那么关闭它有什么意义吗? (或担心关闭它) CharStreams.toString(InputSupplier) 现在已弃用。我创建了一个 CharSource(从一个使用 asCharSource 的 ByteSource),然后按照文档的建议使用它的 toString。 @TedM.Young:如果您只有一个InputStream
,并且您想将其作为String
获得,那么CharStreams.toString(new InputStreamReader(inputStream, charset))
是您的最佳选择。 ByteSource
和 CharSource
专门用于您拥有可以充当 InputStream
s 或 Reader
s 来源的东西的情况。【参考方案4】:
根据接受的答案,这是一个模拟IOUtils.toString()
行为的实用方法(以及带有字符集的重载版本)。这个版本应该是安全的吧?
public static String toString(final InputStream is) throws IOException
return toString(is, Charsets.UTF_8);
public static String toString(final InputStream is, final Charset cs)
throws IOException
Closeable closeMe = is;
try
final InputStreamReader isr = new InputStreamReader(is, cs);
closeMe = isr;
return CharStreams.toString(isr);
finally
Closeables.closeQuietly(closeMe);
【讨论】:
对我来说看起来很好。如果您学会从可重用的输入供应商而不是一次性流和读取器(如果可能的话)的角度来思考,Guava 的 IO 东西效果最好,但我想因为您正在转换现有的 IOUtils 代码,这将是一个很大的变化。跨度> 在我的番石榴 14 中,closeQuietly 已被弃用。建议使用 Java 7 中存在的 try-with-resources 功能。更多信息请访问code.google.com/p/guava-libraries/wiki/… @AlbertKam 同意了。但请记住:这个答案是三年前的。 @SeanPatrickFloyd:谢谢!实际上,我从您的回答开始得到了更新的解决方案。我正在考虑为可能正在使用较新版本的其他人添加评论。 :)【参考方案5】:更新:回想起来,我不喜欢我的旧解决方案。此外,现在是 2013 年,Java7 现在有更好的替代品。所以这是我现在使用的:
InputStream fis = ...;
String text;
try ( InputStreamReader reader = new InputStreamReader(fis, Charsets.UTF_8))
text = CharStreams.toString(reader);
或者如果有 InputSupplier
InputSupplier<InputStreamReader> spl = ...
try ( InputStreamReader reader = spl.getInput())
text = CharStreams.toString(reader);
【讨论】:
【参考方案6】:EDIT (2015):Okio 是我所知道的 Java/android 中最好的 I/O 抽象和工具。我一直在使用它。
FWIW 这是我使用的。
如果我手头已经有一个流,那么:
final InputStream stream; // this is received from somewhere
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>()
public InputStream getInput() throws IOException
return stream;
, Charsets.UTF_8));
如果我正在创建一个流:
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>()
public InputStream getInput() throws IOException
return <expression creating the stream>;
, Charsets.UTF_8));
作为一个具体的例子,我可以像这样读取一个 Android 文本文件资产:
final Context context = ...;
String s = CharStreams.toString(CharStreams.newReaderSupplier(new InputSupplier<InputStream>()
public InputStream getInput() throws IOException
return context.getAssets().open("my_asset.txt");
, Charsets.UTF_8));
【讨论】:
现在全部弃用。 :( 试试github.com/square/okio吧——我已经有一段时间没用过Guava的I/O了,Okio简直更好,【参考方案7】:另一种选择是从 Stream 中读取字节并从中创建一个字符串:
new String(ByteStreams.toByteArray(inputStream))
new String(ByteStreams.toByteArray(inputStream), Charsets.UTF_8)
它不是“纯”番石榴,但它更短一些。
【讨论】:
不幸的是,ByteStreams.toByteArray()
没有关闭流,根据 Javadoc。
确实如此。我还没有看到任何关闭流的番石榴功能。好吧,除了 closeQuietly。
通常,流在 try-with-resources 语句中打开并自动关闭,因此它不应该由 toByteArray() 负责【参考方案8】:
举个具体的例子,下面是我如何读取 Android 文本文件资源:
public static String getAssetContent(Context context, String file)
InputStreamReader reader = null;
InputStream stream = null;
String output = "";
try
stream = context.getAssets().open(file);
reader = new InputStreamReader(stream, Charsets.UTF_8);
output = CharStreams.toString(reader);
catch (IOException e)
e.printStackTrace();
finally
if (stream != null)
try
stream.close();
catch (IOException e)
e.printStackTrace();
if (reader != null)
try
reader.close();
catch (IOException e)
e.printStackTrace();
return output;
【讨论】:
【参考方案9】:如果输入流来自类路径资源,自动关闭解决方案会更短:
URL resource = classLoader.getResource(path);
byte[] bytes = Resources.toByteArray(resource);
String text = Resources.toString(resource, StandardCharsets.UTF_8);
使用 Guava Resources,灵感来自 IOExplained。
【讨论】:
当被问到这个问题时,Resources 类并不存在,但你是对的:今天这可能是要走的路。谢谢以上是关于IOUtils.toString(InputStream) 的番石榴等价物的主要内容,如果未能解决你的问题,请参考以下文章
输出流FileWriter和FileInputStream,和一个可以处理中文的OutputStreamWriter,如果要对中文进行传输和写入用OutputStreamWriter和InputStr