java - 将二进制文件保存到 OutputStream 并返回到 InputStream

标签 java android

我有一个二进制文件,已上传到云存储。我必须将该文件写入输出流。然后当我想下载文件时,我需要从InputStream中读取它的内容。但是我没有得到相同的文件。以下是完整的工作代码,但存在该错误。排除云和其他代码,因为问题不存在。

我已经阅读了 Java 文档来构造从流的读取和写入,但我找不到问题所在 - 为什么在写入输出流然后从输入流读取后我得到不同的数据。任何建议将不胜感激!

public class TActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Create any binary file, e.g., a DB file
  String dbName = "bed872bc-bc8f-4429-8126-070a48c13bdf"; // Just a random UUID
  try (SQLiteDatabase db = openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null)) { ; };

  try {
   // Write the binary file into Output stream
   File originalFile = getDatabasePath(dbName);
   long originalFileLength = originalFile.length();
   ByteArrayOutputStream memoryStreamOut = new ByteArrayOutputStream();
   WriteFromFile(memoryStreamOut, originalFile.toString());

   // Write from the memory stream into the file - expected result is that we end up with identical file content as the original file
   ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(memoryStreamOut.toByteArray());
   File againTheSameFile = CreateTemporaryFileSafe(this);
   WriteToFile(memoryStreamIn, againTheSameFile);

   // Check whether the files have same size - they should have because they should be identical
   long newFileLength = againTheSameFile.length();
   if (newFileLength != originalFileLength) {
    // it ends up here - they don't equal!
   }
  }
  catch (Exception E) {
   // Log ...
  }
 }

 public static void WriteFromFile(OutputStream outputStream, String filePath) throws Exception {
  try (Writer writer = new OutputStreamWriter(outputStream)) {
   try (FileInputStream fileinputstream = new FileInputStream(filePath)) {
    byte[] buffer = new byte[64 * 1024];
    int length;
    while ((length = fileinputstream.read(buffer)) != -1) {
     writer.write(new String(buffer, 0, length));
    }
   }
  }
 }

 public static void WriteToFile(InputStream inputStream, File file) throws Exception {
  try (DataInputStream reader = new DataInputStream(inputStream)) {
   try (DataOutputStream fileOutputStream = new DataOutputStream(new FileOutputStream(file))) {
    byte[] buffer = new byte[64 * 1024];
    int length;
    while ((length = reader.read(buffer)) != -1) {
     fileOutputStream.write(buffer, 0, length);
    }
   }
  }
 }

 public static File CreateTemporaryFileSafe(Context context) {
  try { return File.createTempFile(UUID.randomUUID().toString(), "tmp", context.getCacheDir()); }
  catch (IOException E) { return null; }
 }

}

最佳答案

简短的回答:您的二进制数据因从字节到字符串然后是字符串到字节的(不必要的)转换而被损坏。相反,只需将其读取为字节,然后将其写入为字节:

import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;

import java.io.OutputStream;
import java.io.FileOutputStream;

class Foo {
    private static void copyStream(InputStream input, OutputStream output) throws IOException {
        byte[] buffer = new byte[64*1024];
        int length;
        while ((length = input.read(buffer)) != -1) {
            output.write(buffer,0,length);
        }
    }

    private static void copyFile(String inputPath, String outputPath) throws IOException {
        try (
            FileInputStream input = new FileInputStream(inputPath);
            FileOutputStream output = new FileOutputStream(outputPath)
        ) {
            copyStream(input, output);
        }
    }

    public static void main(String[] args) throws IOException {
        copyFile(args[0], args[1]);
    }
}

更长的答案是,当您使用 String(bytes,0,len) 将字节转换为字符串时,java 将使用 JVM 的默认字符集(可能是 UTF-8)进行该转换。但您的文件不是 UTF-8 编码文本,而是随机字节。其中许多字节都不是正确的 UTF-8。 Java 会默默地用 unicode 'REPLACMENT CHARACTER' 替换每个非法字节,即\uFFFD。

然后,当您将其写回时,您使用了 OutputStreamWriter(OutputStream),它使用默认字符集将(已损坏的)数据转换回字节。假设默认编码为 UTF-8,则替换字符的每个实例都会替换为该字符的 UTF-8 编码,即字节 0xef 0xbf 0xbd。 IE。将其变成三个字节。这就是文件大小发生变化的原因:输入文件中不符合合法 UTF-8 的每个字节都会在输出文件中更改为三个字节。

关于java - 将二进制文件保存到 OutputStream 并返回到 InputStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48599081/

相关文章:

java - 格式化内存输入

java - 为什么我的下拉菜单可以离线使用,但不能在我的服务器上使用?

java - GWT 找不到我的类的源代码

Android 数据库问题管理日期

java - Android 警报对话框无法找到 View

java - springmvc Controller 公共(public)或默认 protected

java - 如何高效地 dockerized java 微服务

android - Paypal future 付款协议(protocol)页面未在 android 中显示指向商户隐私政策和用户协议(protocol)的链接

android - 如何在flutter中实现实时人脸解锁功能

java - 测验中的进度条 (Android Studio)