Android下载二进制文件问题


71

我在从互联网上下载我的应用程序中的二进制文件时遇到问题。在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();

Answers:


93

我不知道这是否是唯一的问题,但是您在其中遇到了典型的Java故障:您没有指望总是允许read()返回的字节数少于您要求的字节数。因此,您的读取可能会少于1024个字节,但是您的写入操作总是会精确地写出1024个字节,其中可能包括来自先前循环迭代的字节。

更正为:

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

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


4
多么愚蠢的错误...谢谢!如果您没有正确阅读本教程,就会发生这种情况:)
Isaac Waller's

初始化缓冲区呢?防止异常该怎么办?释放资源呢?我认为这是一个很好的答案,但不是一个完整的答案。这里还有其他更完整的答案。
AlikElzin-kilaka 2011年

3
我想指出的是,> 0的测试可能会提前终止读数。文档说,-1在流的末尾返回。
克林特(Clint)

2
@Clint:是,但是文档还说(从Java 5开始),除非'len'参数为0(如果没有可用的字节数(...)-1,则返回0);否则,至少返回读取一个字节)。在Java 2中,可以返回0。
njzk2 2012年

1
你知道你是耶稣吗?您刚刚从地狱救了我:)
codezombie

28
new DefaultHttpClient().execute(new HttpGet("http://www.path.to/a.mp4?video"))
        .getEntity().writeTo(
                new FileOutputStream(new File(root,"Video.mp4")));

1
我也喜欢一种解决方案。但是,您应在写入文件之前检查实体,否则即使下载有问题,也会创建文件。因此,下次您可能会尝试打开损坏的文件。
thanhbinh84

如何动态地将下载的文件命名为与原始文件相同的名称?
Compaq LE2202x 2013年

使用该方法,您需要添加一些内容,例如获取包含文件名的Content-Disposition标头。
njzk2 2013年

太棒了..(+1)..效果很好..下载的pdf也从未损坏..
Rat-a-tat-a-tat Ratatouille 2014年

@doreamon-我如何检查实体?使用其contentLength方法?请协助
Rat-a-t-a-tat Ratatouille

16

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

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

14
 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 2014年

@JBirdVegas不要在主线程上运行与网络相关的操作。请创建一个工作线程。

使用AsyncTask doInBackground进行try {}捕获代码。并从中删除setDoOutput(true)。
AndroidGeek 2015年

3

我根据以前对该线程的反馈修复了代码。我使用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


这样,将下载相同的文件。我的意思是,如果文件已下载,它将发出警报吗?
罗西2012年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.