2011-07-21 46 views
1

情况:我有一个ArrayList<String>包含一堆图像链接(http:/www.foo.com/bar/image1.jpg,http:/ /www.foo.com/bar/image2巴纽,...等)更有效地从URL下载图像

我发现的代码工作片,以便通过一个下载这些文件之一:

public void run() { 
     try { 
      int counter = 1; 
      for (String image : imagesList) { 
       controller.setDownloadStatusTextArea("Downloading image " + counter + " of " + imagesList.size()); 
       URL u = new URL(image); 
       URLConnection uc = u.openConnection(); 
       String contentType = uc.getContentType(); 
       int contentLength = uc.getContentLength(); 
       InputStream raw = uc.getInputStream(); 
       InputStream in = new BufferedInputStream(raw); 
       byte[] data = new byte[contentLength]; 
       int bytesRead; 
       int offset = 0; 
       while (offset < contentLength) { 
        bytesRead = in.read(data, offset, data.length - offset); 
        if (bytesRead == -1) 
         break; 
        offset += bytesRead; 
       } 
       in.close(); 
       if (offset != contentLength) { 
        throw new IOException("Only read " + offset + " bytes; Expected " + contentLength + " bytes"); 
       } 
       String[] tmp = image.split("/"); 
       String filename = tmp[tmp.length - 1]; 
       FileOutputStream out = new FileOutputStream(filename); 
       out.write(data); 
       out.flush(); 
       out.close(); 
       counter++; 
      } 
      controller.setDownloadStatusTextArea("Download complete"); 
     } catch (Exception ex) { 
      controller.setDownloadStatusTextArea("Download failed"); 
     } 
    } 

这是我第一次做这样的事情在Java中,并且我有一种感觉,通过在for循环之外移动一堆变量,该代码可以更加高效。但我不确定哪些可以安全地移出境外,而不会影响功能和/或性能(无论是消极的还是积极的方式)。在这种情况下的洞察力将不胜感激。 另外:我可以指定文件需要下载到的位置吗?现在他们只出现在项目文件夹中,我希望用户能够更改他的下载文件夹。

在此先感谢。

回答

4

这段代码的时间效率不高。

想想这样:即使你抛弃了每一个可用的可用操作码,JVM执行这部分代码所花的时间根本不重要。真正的延迟将会等待数据通过网络到达。

它可能更节省空间,但我不认为这是必要的。

编辑:你可以做什么是使用线程同时下载多个图像。如果上面的代码看起来很复杂,我会建议你不要这样做:花更多的时间来学习你的语言。

+0

因此,我创建并销毁每次迭代中使用的对象并不重要? – Matthias

+0

可能成为问题的唯一对象是数组。如果我没有弄错,Java会初始化该数组,这对大文件可能会造成麻烦。不过,我不认为这是你的情况。 – slezica

+1

在必要之前不要进行优化。如果此代码稍后为您带来麻烦,并且希望使其更快,则可以将缓冲区(数组)放在循环外,将其大小固定在一个合适的数字上,并在每次文件满时写入文件。 – slezica

1

在让用户选择一个目录的Swing应用程序中,实例化一个JFileChoosersetFileSelectionMode(JFileChooser.DIRECTORIES_ONLY)

只要确保每次迭代都正确初始化,就可以将所有变量声明移到循环之外。相对于下载和保存文件所需的时间,您不会节省大量时间。

+0

这是一个Swing应用程序。别担心,所有的验证都在其他地方发生。 – Matthias

+0

更新了我的答案。 – Paul

+0

谢谢。我知道JFileChooser,但不知道DIRECTORIES_ONLY,谢谢。它只是打我,我只需要添加文件名的路径,以将文件保存在不同的位置:) – Matthias

2

你不需要为整个图像分配一个字节数组......你只需要一个小缓冲区 - 例如, 8 kB。 然后,从连接读取8 kB,并在循环中写入FileOutputStream。

为了使整个代码更简单(踢出循环),可以使用例如 Commons-IO (点击FRAMES链接查看整个javadoc)。

+0

好的,谢谢你的提示。这是否真的有很大的不同? – Matthias

+0

@Matthias:如果你下载的图片可能比JVM的可用堆内存大,那么,是的,这肯定会有所作为。否则,最终用户将面临'OutOfMemoryError'。 – BalusC

+0

我正在从公共图像板下载它们,它们都不是非常大。 – Matthias