【问题标题】:Can you create a FileWriter or BufferedWriter directly from an InputStream?您可以直接从 InputStream 创建 FileWriter 或 BufferedWriter 吗?
【发布时间】:2020-05-25 04:04:03
【问题描述】:

所以我使用 Java.net.URL 来连接到包含一些 xml (.atom?) 数据的 URL,用于解析以写入本地文件。

这教会了我很多关于阅读和写作的知识。这是我目前的流程:

  1. 创建 URL 对象
  2. 获取“流”
  3. 创建一个 InputStreamReader
  4. 在缓冲读取器中包装 ISR
  5. 创建 FileWriter
  6. 在 Bufferedwriter 中包装 FW
  7. 一行一行,先读后写
  8. 关闭 BufferedWriter。

有效!我了解了 reader、BufferedReader、writer 和 BfferedWriter 之间的区别。那太棒了。我对流有点不清楚,但现在没关系。

但是,我想知道是否有一种方法可以直接从 InputStream 创建一个 BufferedWriter 以节省所有这些中间步骤。

  1. 创建 URL 对象
  2. 获取“流”
  3. 直接跳到 BufferedWriter()
  4. 写入本地文件。
  5. 关闭 BufferedWriter

我看到 BufferedWriter 构造函数需要一个写入器,所以我假设不是,但我不禁想知道你是否可以减少一些步骤。

【问题讨论】:

  • 您似乎只关心将数据下载到本地文件中。如果是这种情况,那么您不需要使用ReaderWriter,因为您不会尝试将二进制数据解码为字符,反之亦然。将数据(即原始字节)从 InputStream 传输到 OutputStream 就足够了(尽管您可能想要使用缓冲区/缓冲流)。您的应用程序是否还需要使用阅读器和/或编写器?
  • @Slaw 是的,这就是我的问题的核心。我只想将 URL 中的数据直接保存到文件中。我不需要对数据进行任何中间操作。

标签: java file-io


【解决方案1】:

您可以直接从 InputStream 创建 FileWriter 或 BufferedWriter 吗?

没有。正如您所注意到的,BufferedWriter 包装了Writer 的另一个实例。这是Decorator 模式的Java 的quintessential example

装饰者模式的cons 之一是它倾向于产生很多需要组合的小对象。

从好的方面来说,除了教你很多关于阅读和写作的知识外,这个练习现在还教你设计模式!

【讨论】:

  • 谢谢,这是个好消息。这个,加上另一个答案告诉我,我可以只使用输入和输出流来写入原始数据,而无需读取器和写入器。
【解决方案2】:

类似以下内容?

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;

public class Main {
    public static void main(String[] args) {
        try (InputStream inputStream = URI.create("https://www.example.com/").toURL().openStream()) {
            try (FileOutputStream outputStream = new FileOutputStream(new File("output.txt"))) {
                int read;
                byte[] bytes = new byte[1024];
                while ((read = inputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, read);
                }
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

  • 出于好奇,你怎么知道byte[]列表应该初始化到1024?
  • 您可以选择不同的块大小一次性读取。这取决于您的需求,这取决于内存、带宽等因素。
【解决方案3】:

回答您的主要问题:不,您不能直接从InputStream / OutputStream 转到BufferedReader / BufferedWriter。问题是您需要一个可以从直接处理字节的输入/输出流转换为处理字符的读取器/写入器的类(即通过将字节解码为字符,反之亦然)。正如@jaco0646's answer 所指出的,这是装饰器模式的缺点之一。当然,你可以制作一个实用方法:

public static BufferedReader toBufferedReader(InputStream stream, Charset charset) {
  return new BufferedReader(new InputStreamReader(stream, charset));
}

使您的其他代码更短。虽然我确信已经有一个库可以提供这个(例如,可能是 Commons IOGuava)。

话虽如此,您似乎不需要读者或作家。您的问题表明,您稍后在a comment 中确认您只是将数据直接下载到文件中。这意味着您无需将字节解码为字符,只需立即将字符编码回字节。您只需将字节从InputStream 直接传输到OutputStream。以下是一些示例(保留在 JDK 中):

使用缓冲区手动复制字节

public static void copyUrlToFile(URL url, File file) throws IOException {
  try (InputStream input = url.openStream();
      OutputStream output = new FileOutputStream(file)) {

    byte[] buffer = new byte[1024 * 8]; // 8 KiB buffer
    int read;
    while ((read = input.read(buffer)) != -1) {
      output.write(buffer, 0, read);
    }
  }
}

也可以使用PathFiles#newOutputStream(Path,OpenOption...) 代替FileFileOutputStream

使用InputStream#transferTo(OutputStream) (Java 9+)

public static void copyUrlToFile(URL url, File file) throws IOException {
  try (InputStream input = url.openStream();
      OutputStream output = new FileOutputStream(file)) {
    input.transferTo(output);
  }
}

使用Files#copy(InputStream,Path,CopyOption...)

public static void copyUrlToFile(URL url, Path file) throws IOException {
  try (InputStream input = url.openStream()) {
    Files.copy(input, file, StandardCopyOption.REPLACE_EXISTING);
  }
}

请注意,在使用 NIO 时,您可以分别使用 Files#newBufferedReader(Path)Files#newBufferedWriter(Path,OpenOption...) 直接打开缓冲的文件读取器和写入器。

【讨论】:

    猜你喜欢
    • 2013-07-27
    • 2012-09-10
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 2010-12-24
    相关资源
    最近更新 更多