我正在尝试开发一个小型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);
}
}
}
我无法每次都重现错误,似乎有些机器会比其他机器更频繁地生成错误。也许这是一个虚假的闭合手柄或类似的东西?
答案 0 :(得分:0)
如果这是一个恢复下载并且保存的文件不是预期长度,特别是如果它更短但确实存在,那么你有一个未处理的错误情况。我不知道什么是适合你的情况,但我还是要删除文件并重新开始。
如果仅为了清晰起见,您需要进行评论中提到的所有调整。