/******************************************************************************************************************* * Authors: SanAndreasP * Copyright: SanAndreasP * License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International * http://creativecommons.org/licenses/by-nc-sa/4.0/ *******************************************************************************************************************/ package de.sanandrew.core.manpack.managers; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.Observable; /** * contains code from http://stackoverflow.com/a/14069976 */ public class UpdateDownloader extends Observable implements Runnable { // Max size of download buffer. private static final int MAX_BUFFER_SIZE = 1024; private final URL url; // download URL private int size; // size of download in bytes private int downloaded; // number of bytes downloaded private final File modJar; private volatile EnumDlState status; // current status of download private Runnable execWhenSucceed; // Constructor for Download. public UpdateDownloader(URL url, File jar) { this.url = url; this.size = -1; this.downloaded = 0; this.status = EnumDlState.PENDING; this.modJar = jar; // Begin the download. this.download(); } public void setSucceedRunnable(Runnable runnable) { this.execWhenSucceed = runnable; } // Get this download's URL. public String getUrl() { return this.url.toString(); } // Get this download's size. public int getSize() { return this.size; } // Get this download's progress. public float getProgress() { return ((float) this.downloaded / this.size) * 100; } // Get this download's status. public EnumDlState getStatus() { return this.status; } // Pause this download. public void pause() { this.status = EnumDlState.PAUSED; this.stateChanged(); } // Resume this download. public void resume() { this.status = EnumDlState.DOWNLOADING; this.stateChanged(); this.download(); } // Cancel this download. public void cancel() { this.status = EnumDlState.CANCELLED; this.stateChanged(); } // Mark this download as having an error. private void error() { this.status = EnumDlState.ERROR; this.stateChanged(); } // Start or resume downloading. private void download() { Thread thread = new Thread(this); thread.start(); } // Get file name portion of URL. private static String getFileName(URL url) { String fileName = url.getFile(); return fileName.substring(fileName.lastIndexOf('/') + 1); } // Download file. public void run() { try { HttpURLConnection connection = (HttpURLConnection) this.url.openConnection(); connection.setRequestProperty("Range", "bytes=" + this.downloaded + '-'); // Specify what portion of file to download. connection.connect(); if( connection.getResponseCode() / 100 != 2 ) { // Make sure response code is in the 200 range. this.error(); } int contentLength = connection.getContentLength(); // Check for valid content length. if( contentLength < 1 ) { this.error(); } if( this.size == -1 ) { // Set the size for this download if it hasn't been already set. this.size = contentLength; this.stateChanged(); } try( RandomAccessFile file = new RandomAccessFile(new File(this.modJar.getParent(), getFileName(this.url)), "rw"); InputStream stream = connection.getInputStream() ) { file.seek(this.downloaded); // seek to the end of the file. this.status = EnumDlState.DOWNLOADING; this.stateChanged(); while( this.status == EnumDlState.DOWNLOADING ) { byte buffer[]; // Size buffer according to how much of the file is left to download. if( this.size - this.downloaded > MAX_BUFFER_SIZE ) { buffer = new byte[MAX_BUFFER_SIZE]; } else { buffer = new byte[this.size - this.downloaded]; } int read = stream.read(buffer); // Read from server into buffer. if( read == -1 ) { break; } file.write(buffer, 0, read); // Write buffer to file. this.downloaded += read; this.stateChanged(); } } if( this.status == EnumDlState.DOWNLOADING ) { // Change status to complete if this point was reached because downloading has finished. this.status = EnumDlState.COMPLETE; this.stateChanged(); if( !this.modJar.delete() ) { error(); } if( UpdateDownloader.this.execWhenSucceed != null ) { UpdateDownloader.this.execWhenSucceed.run(); } } } catch( IOException | SecurityException e ) { error(); } } private void stateChanged() { this.setChanged(); this.notifyObservers(); } public enum EnumDlState { DOWNLOADING, PAUSED, COMPLETE, CANCELLED, ERROR, PENDING } }