【问题标题】:Guava equivalent for IOUtils.toString(InputStream)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 作为默认版本。
  • @Vadzim 提出这个问题时这些文档不存在:-)

标签: java io inputstream guava


【解决方案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&lt;InputStream&gt; 作为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&lt;InputStream&gt; 并使用了重载CharStreams.toString(InputSupplier&lt;R extends Readable &amp; Closeable&gt;),则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 专门用于您拥有可以充当 InputStreams 或 Readers 来源的东西的情况。
【解决方案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 类并不存在,但你是对的:今天这可能是要走的路。谢谢
      猜你喜欢
      • 2013-07-14
      • 2012-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多