Downloading Multiple Files Parallelly or Asynchronously in Java - java

Here I am trying to download multiple files one after another:
Environment - Java 1.6
public List<Attachment> download(List<Attachment> attachments)
{
for(Attachment attachment : attachments) {
attachment.setDownStatus("Failed");
String destLocation = "C:\Users\attachments";
try {
String attUrl = attachment.getUrl();
String fileName = attachment.getFileName();
URL url = new URL(attUrl);
File fileLocation = new File(destLoc, fileName);
FileUtils.copyURLToFile(url, fileLocation);
if(fileLocation.exists()) {
attachment.setDownStatus("Completed");
}
} catch(Exception e) {
attachment.setDownStatus("Failed");
} finally {
attachment.setDestLocation(destLocation);
}
}
return attachments;
}
I am downloading the file from provided URL (http://cdn.octafinance.com/wp-content/uploads/2015/07/google-hummingbird.jpg).
FileUtils.copyURLToFile(url, fileLocation);
The above code does its downloading job perfectly, without any issues.
My Problem:
If the list of attachments are more it will take more time, so I would like to make it an asynchronous or parallel process instead of downloading sequentially.

Use Java 8 Streams in combination with ForkJoinPool
public List<Attachment> download(List<Attachment> attachments) throws InterruptedException, ExecutionException {
ForkJoinPool forkJoinPool = new ForkJoinPool(attachments.size());
return forkJoinPool.submit(() -> processAttachments(attachments)).get();
}
private List<Attachment> processAttachments(List<Attachment> attachments) {
return attachments.stream().parallel().map(attachment -> processSingleAttachment(attachment)).collect(Collectors.toList());
}
private Attachment processSingleAttachment(Attachment attachment){
//business logic to download single attachment
.
.
}

public List<Attachment> download(List<Attachment> attachments)
{
ExecutorService executorService = Executors.newCachedThreadPool();
for(final Attachment attachment : attachments){
executorService.submit(new Runnable() {
#Override
public void run() {
try{
String attUrl = attachment.getUrl();
String fileName = attachment.getFileName();
String destLocation = "C:\Users\attachments";
URL url = new URL(attUrl);
String fileLocation = new File(destLoc, fileName);
FileUtils.copyURLToFile(url, fileLocation);
if(fileLocation.exists()) {
attachment.setDownStatus("Completed");
}
}
catch(Exception e){
attachment.setDownStatus("Failed");
}
}
});
}
executorService.shutdown();
return attachments;
}

Actually, after carefully looking, Boris' code is faulty and will indeed not set some stuff sometimes. Here's a better version that fixes that:
public List<Attachment> download(List<Attachment> attachments) {
ExecutorService executorService = Executors.newCachedThreadPool();
List<Future<Attachment>> futures = new ArrayList<Future<Attachment>>();
for (final Attachment attachment : attachments) {
futures.add(executorService.submit(new Callable<Attachment>() {
#Override
public Attachment call() throws Exception {
return doDownload(attachment);
}
}));
}
for (Future<Attachment> future: futures) {
try {
future.get();
} catch (Exception ex) {
// Do something
}
}
return attachments;
}
private Attachment doDownload(Attachment attachment) throws Exception {
attachment.setDownStatus("Failed");
attachment.setDestLocation("C:\\Users\\attachments");
String attUrl = attachment.getUrl();
String fileName = attachment.getFileName();
URL url = new URL(attUrl);
File fileLocation = new File(attachment.getDestLocation(), fileName);
FileUtils.copyURLToFile(url, fileLocation);
if (fileLocation.exists()) {
attachment.setDownStatus("Completed");
}
return attachment;
}
However, this is absolutely not optimal given your structure of Attachment and how you use it. I did not fix that: I only answered the question as it was asked.

Related

How to convert this method from using java.io.File to java.nio.file?

Basically I have this method that I got off a tutorial (My main goal is to simply return the images from the spring boot server so that I could dynamically view them in Angular)
#RestController
public class FileController {
#Autowired
ServletContext context;
#GetMapping(path = "/allImages")
public ResponseEntity<List<String>> getImages(){
List<String> images = new ArrayList<String>();
String filesPath = context.getRealPath("/images");
File fileFolder = new File(filesPath);
if(fileFolder!=null) {
for(final File file : fileFolder.listFiles()) {
if(!file.isDirectory()) {
String encodeBase64 = null;
try {
String extention = FilenameUtils.getExtension(file.getName());
FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[(int)file.length()];
encodeBase64 = Base64.getEncoder().encodeToString(bytes);
images.add("data:image/"+extention+";base64,"+encodeBase64);
fileInputStream.close();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
return new ResponseEntity<List<String>>(HttpStatus.OK);
}
With the current code, when I try to return the files, I get:
java.lang.NullPointerException: Cannot read the array length because the return value of "java.io.File.listFiles()" is null
I've been searching around and noticed that people recommend using java.nio.file instead but I'm a little lost on how would I implement this here. Any help is appreciated.
Example with nio:
public List<String> readImages() throws IOException {
return Files.list(Path.of("/images"))
.filter(Files::isRegularFile)
.map(this::encode)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private String encode(Path file) {
try {
String extension = FilenameUtils.getExtension(file.getFileName().toString());
String encodeBase64 = Base64.getEncoder().encodeToString(Files.readAllBytes(file));
return "data:image/"+extension+";base64,"+encodeBase64;
} catch (Exception e) {
return null;
}
}
First get a Path to your folder:
Path folderPath = Paths.get(filesPath);
If your Path points to a directory, you can get a Stream<Path> of its contents using Files.list:
if (Files.isDirectory(folderPath)) {
List<Path> files = Files.list(folderPath)
.filter(path -> !Files.isDirectory(path))
.collect(Collectors.toList());
// Do something with the files.
}
It doesn't look like you are using the FileInputStream for anything, so you shouldn't need to translate that part. To get the file extension of your path, you probably need to convert the Path to a string, and extract the extension yourself.
I solved this issue with this code:
#Autowired
ServletContext context;
#GetMapping(path = "/allImages")
public List<String> readImages() throws IOException {
return Files.list(Paths.get(context.getRealPath("/images")))
.filter(Files::isRegularFile)
.map(this::encode)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private String encode(Path file) {
try {
String extension = FilenameUtils.getExtension(file.getFileName().toString());
String encodeBase64 = Base64.getEncoder().encodeToString(Files.readAllBytes(file));
return "data:image/"+extension+";base64,"+encodeBase64;
} catch (Exception e) {
return null;
}
}
Thank you to everyone who helped.

parallel stream is not working for multiple file upload to aws s3

The following method is written to parallel upload of files to AWS S3, but the parallel processing is not happening. It is taking 30 sec if uploading 30 files, the 50s if uploading 50 files.
I am not sure why the parallel() stream is not working in my use case. Any pointers will be appreciated
public ConcurrentHashMap<String, String> uploadMyFiles(MultipartFile[] files, String prefix) {
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
Stream.of(files).parallel().forEach(file -> {
String fStatus = uploadMyFile(file, prefix);
map.put(file.getOriginalFilename(), fStatus);
});
return fileStatusMap;
}
public String uploadMyFile(final MultipartFile multipartFile, String prefix) {
try {
final File file = convertMPartFileToFile(multipartFile);
uploadToMyBucket("mybucket", file, prefix);
} catch (final AwsServiceException exception) {
return exception.getMessage()
}
return "OK";
}
private File convertMPartFileToFile(final MultipartFile multipartFile) {
final File file = new File(multipartFile.getOriginalFilename());
try (final FileOutputStream outputStream = new FileOutputStream(file)) {
outputStream.write(multipartFile.getBytes());
} catch (final IOException ex) {
LOGGER.error("log error here");
}
return file;
}
private String uploadToMyBucket(final String bucket, final File file, String fileKey) {
PutObjectResponse putObjectResult = s3Client
.putObject(PutObjectRequest.builder().bucket(bucket).key(fileKey).build(), RequestBody.fromFile(file));
final URL myfileUrl = s3Client.utilities().getUrl(GetUrlRequest.builder().bucket(bucket).key(fileKey).build());
return myfileUrl.toString();
}
I would suggest to use TransferManager along with ExecutorService as shown in examples below;
Parallelizing Large Uploads for Speed and Reliability
Multipart Uploads in Amazon S3
Steps for parallelising any piece of code in general (Have not worked with AWS, this is in general),
You need an ThreadPool to which you can submit a collection of Callable<T> for processing.
Piece of code to submit collection of Callable to the ThreadPool. Each Callable will be executed in the thread from the ThreadPool.
Piece of code to retrieve the result from collection of Future<T>.
This can be naively implemented through this example,
public class S09 {
public static void main(String[] args) {
demoParallel();
}
public static void demoParallel() {
ExecutorService eService = Executors.newFixedThreadPool(5);
String[] myArr = new String[] {"A", "B", "C"};
Stream.of(myArr)
.parallel()
.map(S09::generateCallable)
.map(eService::submit)
.map(S09::obtainFuture)
.forEach(System.out::println);
}
public static void demoMethod() {}
private static Callable<String> generateCallable(final String input) {
return () -> {
System.out.println(
"I am waiting here, " + Thread.currentThread() + " for Input String " + input);
Thread.sleep(5000); // someExpensiveOperationHere
return "Return Value of " + input;
};
}
private static String obtainFuture(Future<String> futureString) {
try {
return futureString.get(10, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
return null;
}
}

Implementing "Move" function with VFS

I'm trying to implement a wrapped "move" function with Xodus, but something is not working out right:
#Override
public boolean move(String appId, String name, String targetName) {
final boolean[] success = new boolean[1];
final Environment env = manager.getEnvironment(xodusRoot, appId);
final VirtualFileSystem vfs = manager.getVirtualFileSystem(env);
env.executeInTransaction(
new TransactionalExecutable() {
#Override
public void execute(#NotNull final Transaction txn) {
File file = vfs.openFile(txn, name, false);
InputStream input = vfs.readFile(txn, file);
if(input != null) {
File targetFile = vfs.openFile(txn, targetName, true);
DataOutputStream output = new DataOutputStream(vfs.writeFile(txn, targetFile));
try {
output.write(ByteStreams.toByteArray(input));
} catch (IOException e) {
e.printStackTrace();
}
vfs.deleteFile(txn, name);
success[0] = true;
}
}
});
// vfs.shutdown();
// env.close();
return success[0];
}
The problem is the file gets moved but the byte array is not getting copied, not sure if the problem is because of multiple VFS operation in the same transaction. Can someone give me a hint of why the bytes from the source file are not getting copied properly?
Looks like you are trying to implement another version of VirtualFileSystem.renameFile(..).

Java - HttpServer not serving images

I have a simple setup including a HttpServer with some context to load image and CSS files:
public class Server {
private HttpServer httpServer;
public Server(int port, String path, HttpHandler handler) {
try {
httpServer = HttpServer.create(new InetSocketAddress(port), 0);
httpServer.createContext(path, handler);
// load css files
List<String> cssFiles = new ArrayList<String>();
Files.walk(Paths.get("../css")).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
cssFiles.add(filePath.getFileName().toString());
}
});
for (String cssFile : cssFiles) {
httpServer.createContext("/css/" + cssFile, new CssHandler(cssFile));
}
// load image files
List<String> imgFiles = new ArrayList<String>();
Files.walk(Paths.get("../images")).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
imgFiles.add(filePath.getFileName().toString());
}
});
for (String imgFile : imgFiles) {
httpServer.createContext("/images/" + imgFile, new ImageHandler(imgFile));
}
httpServer.setExecutor(null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
this.httpServer.start();
}
}
In addition to that there is a css handler which works perfectly fine and and an image handler class, which serves images defined in html tags, BUT images which are included via css tag "background-image" cannot be loaded.. why?
ImageHandler:
class ImageHandler implements HttpHandler {
private String img;
public ImageHandler(String img) {
this.img = img;
}
#Override
public void handle(HttpExchange http) throws IOException {
if (http.getRequestMethod().equals("GET")) {
System.out.println("img transfered..." + img);
try {
StringBuilder contentBuilder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader("../images/" + img));
String str;
while ((str = in.readLine()) != null) {
contentBuilder.append(str);
}
in.close();
} catch (IOException e) {
}
String response = contentBuilder.toString();
http.sendResponseHeaders(200, response.length());
OutputStream os = http.getResponseBody();
os.write(response.getBytes());
os.flush();
os.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Sooo,
this works:
<img src="images/example.png"/>
But this one doesn't work:
background-image: url("images/example.png");
Could anybody explain why and suggest how to solve this problem?
Since partial URLs are relative to the source of the style sheet, and your paths structure looks like this:
/images/example.png
/css/example.css
So image URLs in example.css should either be absolute (/images/example.png), or have the correct relative path (../images/example.png).

How to give parameters to a method which implements another method

I used a jersey server and I want that a endpoint redirect to the download of a file depending on parameters.
I have difficulties with the function below :
#GET
#Path("/get/{id}/{chunk}")
public Response getDescription(#PathParam("id") String id, #PathParam("chunk") String chunk) {
{
StreamingOutput fileStream = new StreamingOutput()
{
#Override
public void write(java.io.OutputStream output, String id) throws IOException, WebApplicationException
{
try
{
if (Objects.equals(chunk, new String("init"))) {
java.nio.file.Path path = Paths.get("src/main/uploads/example/frame_init.pdf");
}
else {
java.nio.file.Path path = Paths.get("src/main/uploads/example/"+ id +".pdf");
}
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
catch (Exception e)
{
throw new WebApplicationException("File Not Found !!");
}
}
};
return Response
.ok(fileStream, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = myfile.pdf")
.build();
}
I have a problem with passing parameters to the function write. I have my parameters id and chunk by the endpoint but I can't use it in the write method because it implements StreamingOutput().
How I can handle it ? Thank you
For java, final keyword should solve your problem.
As updated code;
#GET
#Path("/get/{id}/{chunk}")
public Response getDescription(#PathParam("id") final String id, #PathParam("chunk") final String chunk) {
{
StreamingOutput fileStream = new StreamingOutput()
{
#Override
public void write(java.io.OutputStream output, String id2) throws IOException, WebApplicationException
{
try
{
if (Objects.equals(chunk, new String("init"))) {
java.nio.file.Path path = Paths.get("src/main/uploads/example/frame_init.pdf");
}
else {
java.nio.file.Path path = Paths.get("src/main/uploads/example/"+ id2 +".pdf");
}
byte[] data = Files.readAllBytes(path);
output.write(data);
output.flush();
}
catch (Exception e)
{
throw new WebApplicationException("File Not Found !!");
}
}
};
return Response
.ok(fileStream, MediaType.APPLICATION_OCTET_STREAM)
.header("content-disposition","attachment; filename = myfile.pdf")
.build();
}

Categories

Resources