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

Can you create a FileWriter or BufferedWriter directly from an InputStream?

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

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

  1. 创建URL对象
  2. 得到一个"Stream"
  3. 创建一个 InputStreamReader
  4. 在缓冲中包装 ISR Reader
  5. 创建 FileWriter
  6. 在 Bufferedwriter 中包装 FW
  7. 逐行读后写
  8. 关闭 BufferedWriter。

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

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

  1. 创建URL对象
  2. 得到一个"Stream"
  3. 直接跳到 BufferedWriter()
  4. 写入本地文件。
  5. 关闭 BufferedWriter

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

Can you create a FileWriter or BufferedWriter directly from an InputStream?

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

Decorator模式的cons之一是它往往会产生很多需要组合的小对象。

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

类似下面的内容?

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();
        }
    }
}

回答您的主要问题:不,您不能直接从 InputStream / OutputStream 转到 BufferedReader / BufferedWriter。问题是你需要一个 class,它可以从直接处理字节的 input/output 流转换为处理字符的 reader/writer,即通过将字节解码为字符,反之亦然反之亦然)。正如 所指出的,这是装饰器模式的缺点之一。当然,你可以做一个实用方法:

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

使您的其他代码更短。虽然我确定那里已经有一个图书馆提供这个(例如 Commons IO or Guava)。

综上所述,您似乎不需要 reader 或作家。您的问题表明,您稍后在 中确认您只是将数据直接下载到文件中。这意味着您无需将字节解码为字符即可立即将字符编码回字节。您只需要将字节从 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);
    }
  }
}

也可以使用 Path and Files#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) and Files#newBufferedWriter(Path,OpenOption...) 打开缓冲 readers 和直接写入文件。