HTTP-Download - 有时文件没有完整下载,没有错误

时间:2014-03-24 11:04:22

标签: java file download inputstream outputstream

我正在尝试开发一个小型Java下载程序。但有时候,我无法找到什么时候,一个下载丢失了几个百分点。一次下载损坏后,每次下载后也会损坏。我不知道我的问题在哪里,并尝试了一些不同的输出缓冲区,但没有成功。以下是下载主题的来源:

    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);
            }           
        }
    }

我无法每次都重现错误,似乎有些机器会比其他机器更频繁地生成错误。也许这是一个虚假的闭合手柄或类似的东西?

1 个答案:

答案 0 :(得分:0)

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

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