Android下载二进制文件问题

Posted

技术标签:

【中文标题】Android下载二进制文件问题【英文标题】:Android download binary file problems 【发布时间】:2010-10-09 06:10:24 【问题描述】:

我在从 Internet 下载我的应用程序中的二进制文件(视频)时遇到问题。在 Quicktime 中,如果我直接下载它,它可以正常工作,但通过我的应用程序它会变得一团糟(即使它们在文本编辑器中看起来完全一样)。这是一个例子:

    URL u = new URL("http://www.path.to/a.mp4?video");
    HttpURLConnection c = (HttpURLConnection) u.openConnection();
    c.setRequestMethod("GET");
    c.setDoOutput(true);
    c.connect();
    FileOutputStream f = new FileOutputStream(new File(root,"Video.mp4"));


    InputStream in = c.getInputStream();

    byte[] buffer = new byte[1024];
    int len1 = 0;
    while ( (len1 = in.read(buffer)) > 0 ) 
         f.write(buffer);
    
    f.close();

【问题讨论】:

【参考方案1】:

我不知道这是否是唯一的问题,但你有一个经典的 Java 故障:你没有指望 read() 是总是允许返回的事实比您要求的字节少。因此,您的读取可能会得到少于 1024 个字节,但您的写入总是会准确地写出 1024 个字节,可能包括上一次循环迭代中的字节。

正确:

 while ( (len1 = in.read(buffer)) > 0 ) 
         f.write(buffer,0, len1);
 

也许 android 上较高的延迟网络或较小的 3G 数据包大小会加剧这种影响?

【讨论】:

多么愚蠢的错误……谢谢!当您没有正确阅读教程时会发生这种情况:) 如何初始化缓冲区?防止异常呢?释放资源怎么办?我认为这是一个很好但不是完整的答案。这里还有其他更完整的答案。 我想指出 > 0 测试可能会提前结束阅读。文档说 -1 在流的末尾返回。 @Clint :是的,但文档还说(从 java 5 开始),除非“len”参数为 0,否则不能返回 0(如果没有可用的字节(...)-返回 1;否则,至少读取一个字节)。在java 2中,可以返回0。 你知道你是耶稣吗?你刚刚把我从地狱里救了出来:)【参考方案2】:
new DefaultHttpClient().execute(new HttpGet("http://www.path.to/a.mp4?video"))
        .getEntity().writeTo(
                new FileOutputStream(new File(root,"Video.mp4")));

【讨论】:

我也喜欢单线解决方案。但是,您应该在写入文件之前检查实体,否则即使下载有问题也会创建文件。所以下一次,你可能会尝试打开一个损坏的文件。 如何动态命名下载的文件与原文件名相同? 使用该方法,您需要添加一些内容,例如获取包含文件名的 Content-Disposition 标头。 真棒.. (+1).. 这很好用.. 下载的 pdf 也永远不会损坏.. @doreamon - 我如何检查实体?使用它的 contentLength 方法?请帮忙【参考方案3】:

一个问题是您对缓冲区的读取。如果输入流的每次读取都不是 1024 的精确倍数,您将复制错误数据。使用:

byte[] buffer = new byte[1024];
int len1 = 0;
while ( (len1 = in.read(buffer)) != -1 ) 
  f.write(buffer,0, len1);

【讨论】:

【参考方案4】:
 public class download extends Activity 

     private static String fileName = "file.3gp";
     private static final String MY_URL = "Your download url goes here";

     @Override
     public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try 
            URL url = new URL(MY_URL);
            HttpURLConnection c = (HttpURLConnection) url.openConnection();
            c.setRequestMethod("GET");
            c.setDoOutput(true);
            c.connect();

            String PATH = Environment.getExternalStorageDirectory()
                + "/download/";
            Log.d("Abhan", "PATH: " + PATH);
            File file = new File(PATH);
            if(!file.exists()) 
               file.mkdirs();
            
            File outputFile = new File(file, fileName);
            FileOutputStream fos = new FileOutputStream(outputFile);
            InputStream is = c.getInputStream();
            byte[] buffer = new byte[1024];
            int len1 = 0;
            while ((len1 = is.read(buffer)) != -1) 
                fos.write(buffer, 0, len1);
            
            fos.flush();
            fos.close();
            is.close();
         catch (IOException e) 
            Log.e("Abhan", "Error: " + e);
        
        Log.i("Abhan", "Check Your File.");
     

【讨论】:

这个答案不起作用。主线程上的网络连接会抛出android.os.NetworkOnMainThreadException @JBirdVegas 不要在主线程上运行网络相关操作。请创建一个工作线程。 使用 AsyncTask doInBackground 执行 try catch 代码。并从中删除 setDoOutput(true)。【参考方案5】:

我根据之前对该线程的反馈修复了代码。我使用 eclipse 和多个大文件进行了测试。它工作正常。只需将其复制并粘贴到您的环境中,然后更改 http 路径和您希望将文件下载到的位置。

try 
    //this is the file you want to download from the remote server
    String path ="http://localhost:8080/somefile.zip";
    //this is the name of the local file you will create
    String targetFileName
        boolean eof = false;
    URL u = new URL(path);
    HttpURLConnection c = (HttpURLConnection) u.openConnection();
    c.setRequestMethod("GET");
    c.setDoOutput(true);
    c.connect();
    FileOutputStream f = new FileOutputStream(new File("c:\\junk\\"+targetFileName));
        InputStream in = c.getInputStream();
        byte[] buffer = new byte[1024];
        int len1 = 0;
        while ( (len1 = in.read(buffer)) > 0 ) 
        f.write(buffer,0, len1);
                 
    f.close();
     catch (MalformedURLException e) 
    // TODO Auto-generated catch block
    e.printStackTrace();
     catch (ProtocolException e) 
    // TODO Auto-generated catch block
    e.printStackTrace();
     catch (FileNotFoundException e) 
    // TODO Auto-generated catch block
    e.printStackTrace();
     catch (IOException e) 
    // TODO Auto-generated catch block
    e.printStackTrace();

祝你好运 Alireza Aghamohammadi

【讨论】:

这样会下载同一个文件。我的意思是如果文件已经下载,它会发出警报吗?【参考方案6】:

只需使用apache的复制方法(Apache Commons IO)——使用Java的优势!

IOUtils.copy(is, os);

不要忘记在 finally 块中关闭流:

try
      ...
 finally 
  IOUtils.closeQuietly(is);
  IOUtils.closeQuietly(os);

【讨论】:

也不要越过溪流。

以上是关于Android下载二进制文件问题的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向ELF 文件格式 ( 安装 010 Editor 二进制查看工具的 ELF.bt 插件模板 | 安装 ELF.bt 模板 | 打开 ELF 文件 )

Android 逆向ELF 文件格式 ( 安装 010 Editor 二进制查看工具的 ELF.bt 插件模板 | 安装 ELF.bt 模板 | 打开 ELF 文件 )

如何使用 Retrofit 库在 Android 中下载文件?

开发环境010 Editor 工具 ( 工具下载 | 二进制文件分析模板模板安装 | 快捷键查看与设置 )

开发环境010 Editor 工具 ( 工具下载 | 二进制文件分析模板模板安装 | 快捷键查看与设置 )

将busybox推入android模拟器