2014-03-24 97 views
2

我正在尝试开发一个小的Java下载器。但有时候,我无法找出什么时候,一个下载缺少几个百分点。每次下载之后,一次下载就会损坏,之后它也将被损坏。我不知道我的问题在哪里,并尝试了一些不同的输出缓冲区,但没有成功。这里是下载线程的来源:HTTP-下载 - 有时文件没有被下载完成没有错误

private void startDownload() { 
     try { 
      ins = null; 
      outb=null; 
      setRunning(true); 
      // Erstelle HttpURLConnection Objekt zur Anfrage an den Server 
      con = (HttpURLConnection)url.openConnection(); 
      con.setRequestMethod("GET"); 
      con.setRequestProperty("User-Agent", DataController.getInstance().getUserAgentString()); 
      if (DataController.getInstance().getCookie() != null) { 
       con.setRequestProperty("Cookie", DataController.getInstance().getCookie()); 
      } 

      System.out.println("saveto length: " + this.saveTo); 
      System.out.println("loaded bytes : " + loadedbytes); 

      if (loadedbytes > 0) { 
       // Ist Zieldatei noch vorhanden und stimmt die Größe? 
       if (saveTo != null && saveTo.exists() && saveTo.length() == loadedbytes) { // SAVE TO NULL!!! 
        // Wiederaufnahme-Position übermitteln 
        con.setRequestProperty("Range", "bytes=" + loadedbytes + "-"); 
       } 
       else if (!saveTo.exists()) { 
        // Zieldatei existiert nicht (mehr) 
        try { 
         if (!saveTo.createNewFile()) { 
          // Zieldatei konnte nicht angelegt werden 

          // saveTo-Objekt zurücksetzen und somit Save-Dialog provozieren 
          saveTo = null; 
         } 
        } catch (Exception e) { 
         // Beim Anlegen des Files ist ein unerwarteter Fehler aufgetreten 
         e.printStackTrace(); 

         // saveTo-Objekt zurücksetzen und somit Save-Dialog provozieren 
         saveTo = null; 
        } 
       } 
      } 
      con.connect(); 

      // Prüfe Response-Code 
      rc = con.getResponseCode(); 

      if (DataController.getInstance().isDebugMode()) System.out.println("rc " + rc); 

      if (rc == HttpURLConnection.HTTP_OK || rc == HttpURLConnection.HTTP_PARTIAL) { 

       // Wenn keine 206 Partial Content kam, loaded-Zähler zurücksetzen, download von vorne beginnen 
       if (rc == HttpURLConnection.HTTP_OK) { 
        loadedbytes = 0; 
       } 

       String filename = getFileName(); 

       if (loadedbytes == 0) { 
        size = Long.valueOf(con.getHeaderField("Content-Length")).longValue(); 
       } else { 
        size = loadedbytes + Long.valueOf(con.getHeaderField("Content-Length")).longValue(); 
       } 

       // Fortschritts-Event an alle Abonnenten senden 
       for (DownloadProgressListener l: progressListeners) { 
        l.setMinimum(0); 
        l.setMaximum(size); 
        l.setValue(loadedbytes); 
        l.setFilename(filename); 
        l.setStatus(DownloadProgressListener.STATUS_RUNNING); 
       } 

       // Autosave - Wenn defaultSavePath gesetzt, baue Dateipfad zusammen 
       if (saveTo == null && DataController.getInstance().getSettingsController().getDefaultSavePath() != null 
        && DataController.getInstance().getSettingsController().getUseDefaultSavePath()) { 

        File saveTmp = new File(DataController.getInstance().getSettingsController().getDefaultSavePath(), filename); 

        // Prüfe auf Gültigkeit, wenn gültig - übernehmen 
        if (saveTmp.exists() || saveTmp.createNewFile()) { 
         saveTo = saveTmp; 

         // Filename-Event auslösen, damit Server den "neuen" Zielpfad mitgeteilt bekommt 
         for (DownloadProgressListener l: progressListeners) { 
          l.setFilename(saveTo.getName()); 
         } 
        } 
       } 

       // Wurde der Speicherort (immer)noch nicht festgelegt? 
       if (saveTo == null) { 

        // Speichern-Unter Dialog öffnen 
        FileDialog dia = new FileDialog(ViewController.getFrame(), "Save as", FileDialog.SAVE); 
        dia.setFile(filename); 
        dia.setVisible(true); 

        //System.out.println("now: " + new Date().getTime()); 

        if (dia.getFile() == null) { 
         // User hat Abbrechen gedrückt - Download abbrechen und aus der Liste entfernen 
         if (DataController.getInstance().isDebugMode()) 
          System.out.println("download cancelled"); 

         DataController.getInstance().removeDownload(url); 
         return; 
        } 

        // Settings-Panel bescheidsagen, dass ein Pfad ausgewählt wurde -> DefaultSavePath-Haken anzeigen 
        DataController.getInstance().getSettingsController().setLastSavePath(new File(dia.getDirectory())); 

        saveTo = new File(dia.getDirectory(), dia.getFile()); 

        if (DataController.getInstance().isDebugMode()) 
         System.out.println("Saving to " + saveTo.getAbsolutePath()); 


        // Neuen Dateinamen an Event-Abonnenten propagieren 
        for (DownloadProgressListener l: progressListeners) { 
         l.setFilename(saveTo.getName()); 
        } 

       } 
       // Erstelle einen neuen Thread, der nun unabhängig von der Oberfläche im Hintergrund den Download abarbeitet 
       downloadThread = new Thread(new Runnable() { 

        @Override 
        public void run() { 
          try { 
           // Netzwerk-Stream öffnen 
           //ins = con.getInputStream(); 
           // Dateiausgabe-Stream öffnen, rc == HttpURLConnection.HTTP_PARTIAL => append 
           // outb = new BufferedOutputStream(new FileOutputStream(saveTo, rc == HttpURLConnection.HTTP_PARTIAL)); 
           outb = new FileOutputStream(saveTo, rc == HttpURLConnection.HTTP_PARTIAL); 
           bos = new BufferedOutputStream(outb); 
           bis = new BufferedInputStream(con.getInputStream()); 
           loadedbytes = saveTo.length(); 
           byte data [] = new byte[1024]; 
           int count; 
           long bytesread = 0; 
           long lastTime = System.currentTimeMillis(); 
           long now = 0; 
           int bytespersecond = 0; 
           long timediff = 0; 
           int timeleft = 0; 
           System.out.println("First Size loadedbytes "+loadedbytes); 
           // Lese-Schleife 
           //for (count = ins.read(data,0,1024); count >= 0; count = ins.read(data,0,1024)) 
           while ((count = bis.read(data,0,1024)) > -1) 
           { 
            // Wenn download pausiert oder abgebrochen -> Schleife abbrechen 
            if (stop || paused) break; 

            // Daten schreiben, zähler erhöhen 
            bos.write(data,0,count); 
            bytesread += count; 
            loadedbytes += count; 
            now = System.currentTimeMillis(); 
            timediff = System.currentTimeMillis() - lastTime; 
            // Mehr als 1 Sekunde vergangen 
            if (timediff > 1000L) { 

             // Geschwindigkeit und Restzeit berechnen 
             bytespersecond = (int)Math.round(((double)bytesread/(timediff)) * 1000.0); 
             bytesread = 0; 
             lastTime = now; 
             timeleft = (int)Math.round((double)(size - loadedbytes)/bytespersecond); 

             // Fortschritts-Events auslösen 
             for (DownloadProgressListener l: progressListeners) { 
              l.setValue(loadedbytes); 
              l.setSpeed(bytespersecond); 
              l.setTimeEstimated(timeleft); 
             } 
             ViewController.refreshMainProgress(); 
            } 
           } 
           //Date timer = new Date(); 
           // Ausgabe- und Netzwerk-Stream beenden 
           System.out.println("geladen: " + loadedbytes); 
           bos.flush(); 
           bos.close(); 
           bis.close(); 
           outb.flush(); 
           outb.close(); 
           ins.close(); 
           for (DownloadProgressListener l: progressListeners) 
           { 
            l.setStatus(DownloadProgressListener.STATUS_CHECKSUM_VERIFY); 
           } 
           ViewController.refreshMainProgress(); 

           if (!paused) { 

             // wurde abgebrochen? 
             if (stop) { 

              // Zieldatei löschen, zähler zurücksetzen 
              saveTo.delete(); 
              saveTo = null; 
              loadedbytes = 0; 
              stop = false; 
             } 
             urlMD5 = DataController.getURLMD5(url.toString()); 
             fileMD5 = DataController.getMD5(saveTo.getCanonicalPath()); 
             if(fileMD5!=null && !fileMD5.equals("") 
               && urlMD5!=null && !urlMD5.equals("") && fileMD5.equals(urlMD5)) 
              // MD5 is correct 
             { 
              // Finished-Event auslösen 
              for (DownloadProgressListener l: progressListeners) { 
               l.setChecksum(""); 
               l.setValue(size); 
               l.setStatus(DownloadProgressListener.STATUS_FINISHED); 
              } 
              ViewController.refreshMainProgress(); 
              setRunning(false); 
             }else 
             { // Error-MD5 
              for (DownloadProgressListener l: progressListeners) 
              { 
               l.setChecksum(""); 
               l.setErrorMessage("Error MD5 incorrect: fileMD5:'" + fileMD5 + "' urlMD5:'" + urlMD5 + "'"); 
               l.setStatus(DownloadProgressListener.STATUS_ERROR); 
              } 
              ViewController.refreshMainProgress(); 
              /*loadedbytes = 0; 
              retryCount++; 
              saveTo.delete(); 
              Thread.sleep(1000); 
              if (retryCount < retryMax) 
              { 
               startDownload(); 
               for (DownloadProgressListener l: progressListeners) 
               { 
                l.setStatus(DownloadProgressListener.STATUS_RUNNING); 
               } 
              }else 
              { 
               DataController.getInstance().downloadsRunning.remove(url); 
               loadedbytes = 0; 
               retryCount = 0; 
               outb.close(); 
               saveTo.delete(); 
              }*/ 
             } 
           } 
           else { 
            // Pause-Event auslösen 
            for (DownloadProgressListener l: progressListeners) { 
             l.setStatus(DownloadProgressListener.STATUS_PAUSED); 
            } 
           } 


          ViewController.refreshMainProgress(); 
          //}catch (InterruptedException e) { 
          // e.printStackTrace(); 
          } catch (SSLException e) { 

           try { 
            loadedbytes = 0; 
            retryCount++; 
            outb.close(); 
            bos.close(); 
            saveTo.delete(); 

            if (retryCount < retryMax) { 
             System.out.println("Error: " + e.getLocalizedMessage() + "\n--> Attempt #" + retryCount + ": Retrying file '" + saveTo.getName() + "' in 2 seconds."); 

             Thread.sleep(2000); // 2s 
             startDownload(); 
            } else { 
             // Error-Event auslösen 
             for (DownloadProgressListener l: progressListeners) { 
              l.setErrorMessage("Error retreiving network stream [" + e.getMessage() + "]"); 
              l.setStatus(DownloadProgressListener.STATUS_ERROR); 
             } 
             loadedbytes = 0; 
             retryCount = 0; 
             outb.close(); 
             bos.close(); 
             saveTo.delete(); 
            } 
           } catch (Exception e1) { 
            e1.printStackTrace(); 
           } 
          } catch (IOException e) { 
           e.printStackTrace(); 

           // Error-Event auslösen 
           for (DownloadProgressListener l: progressListeners) { 
            l.setErrorMessage("Error retreiving network stream [" + e.getMessage() + "]"); 
            l.setStatus(DownloadProgressListener.STATUS_ERROR); 
           } 

           try { 
            loadedbytes = 0; 
            retryCount = 0; 

            // Ausgabe- und Netzwerk-Stream beenden, Zieldatei löschen 
            bos.flush(); 
            bos.close(); 
            bis.close(); 
            outb.flush(); 
            outb.close(); 
            ins.close(); 
            saveTo.delete(); 
           } catch (IOException e1) {} 

          } catch (SecurityException e) { 
           e.printStackTrace(); 

           loadedbytes = 0; 
           retryCount = 0; 
           // Error-Event auslösen 
           for (DownloadProgressListener l: progressListeners) { 
            l.setErrorMessage("Error accessing file '" + saveTo.getAbsolutePath() + "'"); 
            l.setStatus(DownloadProgressListener.STATUS_ERROR); 
           } 
          } 
         } 
       }); 
       downloadThread.start(); 
      } else { 
       String errMsg = "HTTP Error Status: " + rc + " " + con.getResponseMessage(); 
       for (DownloadProgressListener l: progressListeners) { 
        l.setErrorMessage(errMsg); 
        l.setStatus(DownloadProgressListener.STATUS_ERROR); 
       } 

       saveTo.delete(); 
       loadedbytes = 0; 
       retryCount = 0; 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 

      loadedbytes = 0; 
      retryCount = 0; 
      try { 
       // Ausgabe- und Netzwerk-Stream beenden 
       bos.flush(); 
       bos.close(); 
       bis.close(); 
       outb.flush(); 
       outb.close(); 
       ins.close(); 
      } catch (Exception e1) {} 

      String errMsg = "Unknown Error"; 
      if (e instanceof MalformedURLException) { 
       errMsg = "Invalid URL"; 
      } else if (e instanceof UnknownHostException) { 
       errMsg = "Unable to resolve hostname '" + e.getMessage() + "'. Please check your network connection."; 
      } else if (e instanceof IOException) { 
       errMsg = e.getMessage(); //"Error retreiving network stream"; 
      } 
      System.out.println(errMsg); 

      // Error-Event auslösen 
      for (DownloadProgressListener l: progressListeners) { 
       l.setErrorMessage(errMsg); 
       l.setStatus(DownloadProgressListener.STATUS_ERROR); 
      }   
     } 
    } 

我不能每次都重现错误,似乎有些机器会像其他机器一样产生更多的错误。也许这是一个错误的关闭句柄或类似的东西?

+0

尝试将更多跟踪日志放入代码中。尤其是在中止循环(如果停止或暂停)的情况下。日期现在可以替换为System.currentTime .. –

+0

顺便说一句,为什么你得到con.getInputStream()两次? –

+0

你不需要所有这些刷新和关闭。只需关闭BufferedOutputStream和BufferedInputStream即可。这里没有任何东西可以破坏数据,除非可能是恢复下载,如果你没有在正确的地方恢复,或者你在发生异常时没有正确关闭。 *你的异常处理是什么? – EJP

回答

0

如果这是一个恢复下载并且保存的文件不是预期的长度,特别是如果它较短但它确实存在,那么您有一个未处理的错误情况。我不知道你的情况是什么,但我伤口正在删除文件,并重新开始。

如果只是为了清晰起见,您需要对我的评论中提到的所有调整进行调整。