[FEATURE] AsyncDownloader теперь стандарт

This commit is contained in:
Gravit 2019-12-11 08:16:06 +07:00
parent 117b95d3fc
commit 0922c18b22
No known key found for this signature in database
GPG key ID: 061981E1E85D3216

View file

@ -0,0 +1,132 @@
package pro.gravit.launcher;
import pro.gravit.utils.helper.IOHelper;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
public class AsyncDownloader {
public AsyncDownloader(Callback callback) {
this.callback = callback;
}
public AsyncDownloader() {
callback = (ignored) -> {};
}
@FunctionalInterface
public interface Callback
{
void update(long diff);
}
public final Callback callback;
public static class SizedFile
{
public final String path;
public final long size;
public SizedFile(String path, long size) {
this.path = path;
this.size = size;
}
}
public void downloadFile(URL url, Path target, long size) throws IOException
{
URLConnection connection = url.openConnection();
try(InputStream input = connection.getInputStream())
{
transfer(input, target, size);
}
}
public void downloadFile(URL url, Path target) throws IOException
{
URLConnection connection = url.openConnection();
try(InputStream input = connection.getInputStream())
{
IOHelper.transfer(input, target);
}
}
public void downloadListInOneThread(List<SizedFile> files, String baseURL, Path targetDir) throws URISyntaxException, IOException {
URI baseUri = new URI(baseURL);
String scheme = baseUri.getScheme();
String host = baseUri.getHost();
int port = baseUri.getPort();
if (port != -1)
host = host + ":" + port;
String path = baseUri.getPath();
for(AsyncDownloader.SizedFile currentFile : files)
{
URL url = new URI(scheme,host,path + currentFile.path, "", "").toURL();
downloadFile(url, targetDir.resolve(currentFile.path), currentFile.size);
}
}
public List<List<SizedFile>> sortFiles(List<SizedFile> files, int threads)
{
files.sort(Comparator.comparingLong((f) -> -f.size));
List<List<SizedFile>> result = new ArrayList<>();
for(int i=0;i<threads;++i) result.add(new LinkedList<>());
long[] sizes = new long[threads];
Arrays.fill(sizes, 0);
for(SizedFile file : files)
{
long min = Long.MAX_VALUE;
int minIndex = 0;
for(int i=0;i<threads;++i)
if(sizes[i] < min) { min = sizes[i]; minIndex = i;}
result.get(minIndex).add(file);
sizes[minIndex]+=file.size;
}
return result;
}
public CompletableFuture[] runDownloadList(List<List<SizedFile>> files, String baseURL, Path targetDir, Executor executor) {
int threads = files.size();
CompletableFuture[] futures = new CompletableFuture[threads];
for(int i=0;i<threads;++i)
{
List<SizedFile> currentTasks = files.get(i);
futures[i] = CompletableFuture.runAsync(() -> {
try {
downloadListInOneThread(currentTasks, baseURL, targetDir);
} catch (URISyntaxException | IOException e) {
throw new CompletionException(e);
}
}, executor);
}
return futures;
}
public void transfer(InputStream input, Path file, long size) throws IOException {
try (OutputStream fileOutput = IOHelper.newOutput(file)) {
long downloaded = 0L;
// Download with digest update
byte[] bytes = IOHelper.newBuffer();
while (downloaded < size) {
int remaining = (int) Math.min(size - downloaded, bytes.length);
int length = input.read(bytes, 0, remaining);
if (length < 0)
throw new EOFException(String.format("%d bytes remaining", size - downloaded));
// Update file
fileOutput.write(bytes, 0, length);
// Update state
downloaded += length;
//totalDownloaded += length;
callback.update(length);
}
}
}
}