0

I'm trying to code based on the manual operation. For manual, I have a URL and when I paste the URL to the Chrome browser, the browser automatically downloads the PDF file from that URL and save to folder "download" without prompting any user input. With Code, I'm able to accomplish the same thing as the manual operation. However I would like the code to save the PDF into specific folder instead of default folder "download". Is it possible to do that?

    public static void browseURL() {
    try {
            
        String url ="mycompanyURL";
        System.out.println("url " + url );
        
        Desktop desktop = Desktop.getDesktop();
        URI uri = new URI (url);            
        desktop.browse(uri);
        
        
    }catch(Exception err) {

        System.out.println("exception " + err.getMessage());
    }
  }
Phil
  • 339
  • 2
  • 13
  • 1
    Your best bet would be to use a http client library (java 11+ comes with a built in one) and download the file that way, then save it where ever you want. using desktop.browse() just launches an external program, you have no control over that. – Anthony Cathers Apr 19 '22 at 21:21
  • great! thank you for suggestion. But the file i'm reading is PDF with image logo too. Will this work with http client library? – Phil Apr 19 '22 at 21:31
  • @KJ great! alternative. I will explore that – Phil Apr 19 '22 at 21:40

2 Answers2

1

When I had to do that in old versions of Java, I used the following snippet (pure Java, source: Baeldung).

public void streamFromUrl(String downloadUrl, String filePath) throws IOException {
    File file = new File(filePath);
    try (BufferedInputStream in = new BufferedInputStream(new URL(downloadUrl).openStream());
         FileOutputStream fileOutputStream = new FileOutputStream(file)) {
        byte[] dataBuffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
            fileOutputStream.write(dataBuffer, 0, bytesRead);
        }
    }
}

The above opens an input stream on the URL, and outputs the bytes of such stream into a file output stream (where the file is wherever you wish).

Alternatively, there are many libraries doing that in one/two liners (the article I posted shows some of those alternatives).

Also, starting from more recent versions of Java, there are other shorter options:

public void streamFromUrl(String downloadUrl, String filePath) throws IOException {
    try (InputStream in = new URL(downloadUrl).openStream()) {
        Files.copy(in, Paths.get(new File(filePath)), StandardCopyOption.REPLACE_EXISTING);
    }
}

Depending on the version of Java you have, you may pick one of those. Generally speaking, I suggest you reading through the Baeldung's article and check the one that best suits for you.

Matteo NNZ
  • 11,930
  • 12
  • 52
  • 89
  • will this work with image PDF because inside the PDF there is barcode image plus company logo, header and footer? – Phil Apr 19 '22 at 21:33
  • 1
    @Phil have you tried it out? As long as you don't have any authentication to perform, it should work. Else,.you'll have to go through the Http approach. But either way, it all turns into streaming bytes in and out, no matter what the content of the file is (it's always bytes in the end) – Matteo NNZ Apr 19 '22 at 21:33
  • no i haven't tried your sample code yet, but I will try out now and hope i won't run into security issue. thanks. – Phil Apr 19 '22 at 21:41
  • 1
    @Phil the only security concern here is about what you download. If you don't control the input URL, someone may ask you to download dangerous content in your server. But that wouldn't change on the method you use to perform the download, the risk would still be the same. – Matteo NNZ Apr 19 '22 at 21:50
  • just try your sample code but run into exception: "unable to find valid certification path to requested target" – Phil Apr 19 '22 at 22:15
  • 1
    @Phil that means you're trying to download a file which is hosted by a server that needs an SSL certificate to trust. [Check this](https://stackoverflow.com/questions/9210514/unable-to-find-valid-certification-path-to-requested-target-error-even-after-c) – Matteo NNZ Apr 20 '22 at 07:29
  • @MatteoNNZ In recent versions of Java there's no need for the loop with the `dataBuffer`; you can simply write `in.transferTo(fileOutputStream)`. – David Conrad Apr 20 '22 at 13:55
  • @DavidConrad yep, I mentioned the other solutions in the reference link but I didn't know the transferTo, nice one. – Matteo NNZ Apr 20 '22 at 14:30
1

Here you go. Handles redirects and so on can use and modify as you wish. Have fun with it. All in native Java. Did write this to download some media easily. This can also download media like images, videos and documents.

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Files;
import java.nio.file.Path;

public class Downloader {
    public static void download(String url) {
        final HttpClient hc = HttpClient.newHttpClient();
        final Builder requestBuilder = HttpRequest.newBuilder().version(HttpClient.Version.HTTP_1_1);
        
        Path path = Path.of("myfilepath");
        handleGet(hc, "myfile.pdf", "myurl.com", path, requestBuilder);
        
    }

    private static void handleGet(
                final HttpClient hc, 
                final String fileName, 
                final String url,
                final Path filePath, 
                final Builder requestBuilder
                ) {
            
            final HttpRequest request = requestBuilder.uri(URI.create(url)).build();
            hc.sendAsync(request, BodyHandlers.ofInputStream())
            .thenApply(resp -> {
                int sc = resp.statusCode();
                System.out.println("STATUSCODE: "+sc+" for url '"+url+"'");
                if(sc >= 200 && sc < 300) return resp;
                if(sc == 302) {                 
                    System.out.println("Handling 302...");
                    String newUrl = resp.headers().firstValue("location").get();
                    
                    handleGet(hc, fileName, newUrl, filePath, requestBuilder);
                }
                return resp;
            })
            .thenAccept(resp -> {
                int sc = resp.statusCode();
                if(sc >= 200 && sc < 300) {                 
                    try {
                        System.out.println("Im fine here");
                        Files.copy(resp.body(), filePath);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    System.err.println("STATUSCODE: "+ sc +" for file "+ fileName);
                }
            }).join();
        }
}
Levent Dag
  • 162
  • 8