JavaFX WebView Downloading - java

I'm trying to add downloads to my Web Browser but the problem I got is to get the name of the file that you're trying to download. This is my code for downloading:
engine.locationProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
File file = new File(System.getProperty("user.home") + "/Downloads/Ekko Downloads/");
String[] downloadableExtensions = {".doc", ".xls", ".zip", ".exe", ".rar", ".pdf", ".jar", ".png", ".jpg", ".gif"};
for(String downloadAble : downloadableExtensions) {
if (newValue.endsWith(downloadAble)) {
try {
if(!file.exists()) {
file.mkdir();
}
File download = new File(file + "/" + newValue);
if(download.exists()) {
Dialogs.create().title("Exists").message("What you're trying to download already exists").showInformation();
return;
}
Dialogs.create().title("Downloading").message("Started Downloading").showInformation();
FileUtils.copyURLToFile(new URL(engine.getLocation()), download);
Dialogs.create().title("Download").message("Download is completed your download will be in: " + file.getAbsolutePath()).showInformation();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}
});
The problem is here: File download = new File(file + "/" + newValue);
Instead of that newValue i need to get the name of that file.

Ideally what you would be doing is intercepting calls at the network layer, and interpreting the content disposition MIME messages embedded in the HTTP traffic. Those messages can instruct the browser to download the file as an attachment with a provided filename. That is how you end up with some files being automatically downloaded based on an instruction sent from the server when you click on a link a browser.
Another thing browsers do is implement a kind of mime magic where they look at either the mime content type of the returned message, a deep inspection of the network traffic or just the extension prefix of a URL location to invoke a handler to download specific content types (you are doing only the later in your code).
The last way browsers handle downloads is you can right click on a page or link and choose Save As.
So, if you wanted a really robust fully functional browser like Chrome or Firefox you would do all of the above. As this horribly complicated test matrix shows, it is not really a particularly easy thing to do for all corner cases and even the big guys get it wrong.
Intercepting network traffic for WebView is possible but difficult. You can research other StackOverflow questions to do that - I won't address it here.
The same is also true of intercepting arbitrary web clicks, again search StackOverflow and it will turn up some questions on that, which might allow you to get right click to download functionality working.
So you are left with just intercepting location property changes as you are doing - obviously not ideal, but workable for many scenarios. That means you don't get filenames encoded in the content-disposition header, instead you have to parse the location url (just grab everything after the last /) and set that as the filename.
You can use the answers to the following question to derive the filename from the location URL:
Get file name from URL

The WebView in JavaFX 8.0 will change status to "CANCELLED" when it cannot display a web page. This is generally an indication of a downloadable file, and you can inspect the location to make sure or filter what you want to download.
Next you can create a URL out of the location and do an HTTP HEAD request. This will allow you to get possible options for the filename based of the HTTP headers sent back. The headers may contain a header called Content-Disposition and the content may contain something like the following: attachment; filename="somfilename.ext".
So basically from there you can determine if you want to use the filename in the URL or the filename specified in the Content-Disposition header.

Related

How to download a PDF from Vaadin Flow App?

How would one go about download a pdf in Vaadin? It seems it would have something to do with the anchor button, but don't know how one would go about downloading it.
I have looked at multiple resources, but none have helped. This is a prewritten pdf, not dynamically created, so that removes a bunch of questions. This one is designed around Vaadin7, which does not help me.
If the PDF is among your public, static files, such as in src/main/resources/META-INF/resources in a Spring app, it's as simple as this, where the file path "sample.pdf" is relative to src/main/resources/META-INF/resources.
Anchor anchor = new Anchor("sample.pdf", "Download PDF");
anchor.getElement().setAttribute("download", "downloaded-file-name.pdf");
add(anchor);
Otherwise, you can use this approach. In my case, the file's location is src/main/resources/sample2.pdf.
StreamResource streamResource = new StreamResource("whatever.pdf",
() -> getClass().getResourceAsStream("/sample2.pdf"));
Anchor anchor = new Anchor(streamResource, "Download PDF");
anchor.getElement().setAttribute("download", "downloaded-other-name.pdf");
add(anchor);
Note the leading slash in /sample2.pdf, it's important.
If we don't set the download attribute, the file might be opened instead of downloaded, under the name whatever.pdf.
If we set the download attribute to an empty string, it will be downloaded under the name whatever.pdf. Otherwise, it will be downloaded under the name we provide in the attribute.

Spring MVC upload a file and then provide a link for downloading

I have Spring MVC web app running on Tomcat.
I upload a file and save it in the /tmp folder on the file system.
Then I need to show a link to that file in the view (Thymeleaf), so that the user can download the file by clicking on the link. How to do that?
I've heard about configuring Tomcat to allow a specific context to link to a folder on the FS, but not sure how to do that or if that is the only solution. Please help.
The way I approach this is slightly different. Basically I use two controller actions for handling file uploads, one for uploading, and for downloading (viewing) files.
So upload action would save files to some preconfigured directory on the file system, I assume you already have that part working.
Then declare download action similar to this
#Controller
public class FileController {
#RequestMapping("/get-file/{filename}")
public void getFileAction(#RequestParam filename, HttpServletResponse response) {
// Here check if file with given name exists in preconfigured upload folder
// If it does, write it to response's output stream and set correct response headers
// If it doesn't return 404 status code
}
}
If you want to make impossible to download file just by knowing the name, after uploading file, save some meta info to the database (or any other storage) and assign some hash (random id) to it. Then, in getFileAction, use this hash to look for file, not the original filename.
Finally, I would discourage using /tmp for file uploads. It depends on the system/application used, but generally temp directory are meant, as name suggest, for temporary data. Usually it is guaranteed data in the temp directory will stay for "reasonable time", but applications must take into account that content of temp directory can be deleted anytime.
This is the precisely setup that worked for me (Tomcat 8, SpringMVC, boot):
server.xml:
<Context docBase="C:\tmp\" path="/images" />
In the controller:
public String createNewsSource(#ModelAttribute("newsSource") NewsSource source, BindingResult result, Model model,
#RequestParam("attachment") final MultipartFile attachment) {
new NewsSourceValidator().validate(source, result);
if (result.hasErrors()) {
return "source/addNewSource";
}
if (!attachment.isEmpty()) {
try {
byte[] bytes = attachment.getBytes();
BufferedOutputStream stream = new BufferedOutputStream(
new FileOutputStream(new File("/tmp/" + attachment.getOriginalFilename())));
stream.write(bytes);
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
source.setLogo("images/" + attachment.getOriginalFilename());
newsSourceService.createNewsSourceIfNotExist(source);
return "redirect:/sources/list";
}
As you can see I am saving the file to /tmp, but in the DB (source.setLogo()), I am pointing to images as mapped in server.xml
Here's where I found about Tomcat config:
If the images are all located outside the webapp and you want to have
Tomcat's DefaultServlet to handle them, then all you basically need to
do in Tomcat is to add the following Context element to
/conf/server.xml inside tag:
This way they'll be accessible through http://example.com/images/....
SO answer to a similar question

java.nio.file.Path for URLs?

Java7 ships with a default Path implementation for local files. Is there a Path implementation for URLs?
For example, I should be able to copy a remote resource using the following code:
Path remote = Paths.get(new URI("http://www.example.com/foo/bar.html"));
Path local = Paths.get(new URI("/bar.html"));
Files.copy(remote, local);
Currently, this throws java.nio.file.FileSystemNotFoundException: Provider "http" not installed. I could probably implement this myself but I'd rather not reinvent the wheel.
It seems like what you're really trying to do is accomplish what FTP does - copy files from one place to another. I would suggest you find better ways to do this with existing FTP code libraries.
URIs are not file system paths, so you can't treat them as such. They are addresses/resource locators that, when you go there with your browser (or another client that handles them), they trigger some action as defined by the server that's behind them. There's no standard for what that server does, hence the flexibility of web services. Therefore, if your server is doing to accept HTTP requests in this manner to facilitate file copies, you're going to have to roll your own, and pass the file data into a POST request.
To say it another way, (1) don't treat URIs like they are file system paths - they aren't, (2) find an FTP library to copy files, and/or (3) if you really want to build a web service that does this, abstract the details of the file copying via a POST request. If you do #3 understand that what your building is pretty close to custom, and that it will probably only work on a subset of sites that follow your particular design (i.e. the ones you build yourself). There's no standard set of parameters or "file copying" via POST command that I'm aware of that you can leverage to make this "just work" - you're going to have to match up your HTTP request with the web service on the server side.
You can do:
URI uri = new URI("http://www.example.com/foo/bar.html");
try (InputStream is = uri.toURL().openStream()) {
// ...
}
It will work for http, https and file out of the box, probably for few more.
For relative URIs, you have to resolve them first:
URI relative = new URI("bar.html");
URI base = new URI("http://www.example.com/foo/");
URI absolute = base.resolve(relative);
System.out.println(absolute); // prints "http://www.example.com/foo/bar.html"
Now you can call toURL().openStream() on the absolute URI.
I had to deal with a similar problem and wrote a little method to solve this, you can find below.
It works fine for concatenation of URL and relative suffixes. Be careful not to give suffix absolute because of the behaviour of URI resolve function.
private URI resolveURI(String root, String suffix) {
URI uri;
try {
uri = new URI(root + "/").resolve(suffix);
} catch (URISyntaxException e) {
//log
}
return uri;
}

Java URL problem

A webpage contains a link to an executable (i.e. If we click on the link, the browser will download the file on your local machine).
Is there any way to achieve the same functionality with Java?
Thank you
Yes there is.
Here a simple example:
You can have a JSF(Java Server Faces) page, with a supporting backing bean that contains a method annotated with #PostConstruct This means that any action(for example downloading), will occur when the page is created.
There is already a question very similar already, have a look at: Invoke JSF managed bean action on page load
You can use Java's, URL class to download a file, but it requires a little work. You will need to do the following:
Create the URL object point at the file
Call openStream() to get an InputStream
Open the file you want to write to (a FileOutputStream)
Read from the InputStream and write to the file, until there is no more data left to read
Close the input and output streams
It doesn't really matter what type of file you are downloading (the fact that it's an executable file is irrelevant) since the process is the same for any type of file.
Update: It sounds like what you actually want is to plug the URL of a webpage into the Java app, and have the Java app find the link in the page and then download that link. If that is the case, the wording of your question is very unclear, but here are the basic steps I would use:
First, use steps 1 and 2 above to get an InputStream for the page
Use something like TagSoup or jsoup to parse the HTML
Find the <a> element that you want and extract its href attribute to get the URL of the file you need to download (if it's a relative URL instead of absolute, you will need to resolve that URL against the URL of the original page)
Use the steps above to download that URL
Here's a slight shortcut, based on jsoup (which I've never used before, I'm just writing this from snippets stolen from their webpage). I've left out a lot of error checking, but hey, I usually charge for this:
Document doc = Jsoup.connect(pageUrl).get();
Element aElement = doc.getElementsByTag("a").first() // Obviously you may need to refine this
String newUrl = aElement.attr("abs:href"); // This is a piece of jsoup magic that ensures that the destination URL is absolute
// assert newUrl != null
URL fileUrl = new URL(newUrl);
String destPath = fileUrl.getPath();
int lastSlash = destPath.lastIndexOf('/');
if (lastSlash != -1) {
destPath = destPath.substring(lastSlash);
}
// Assert that this is really a valid filename
// Now just download fileUrl and save it to destPath
The proper way to determine what the destination filename should be (unless you hardcode it) is actually to look for the Content-Disposition header, and look for the bit after filename=. In that case, you can't use openStream() on the URL, you will need to use openConnection() instead, to get a URLConnection. Then you can use getInputStream() to get your InputStream and getRequestProperty("Content-Disposition") to get the header to figure out your filename. In case that header is missing or malformed, you should then fall-back to using the method above to determine the destination filename.
You can do this using apache commons IO FileUtils
http://commons.apache.org/io/apidocs/org/apache/commons/io/FileUtils.html#copyURLToFile(java.net.URL, java.io.File)
Edit:
I was able to successfully download a zip file from source forge site (it is not empty), It did some thing like this
import java.io.File;
import java.net.URL;
import org.apache.commons.io.FileUtils;
public class Test
{
public static void main(String args[])
{
try {
URL url = new URL("http://sourceforge.net/projects/gallery/files/gallery3/3.0.2/gallery-3.0.2.zip/download");
FileUtils.copyURLToFile(url, new File("test.zip"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I was able successfully download tomcat.exe too
URL url = new URL("http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.16/bin/apache-tomcat-6.0.16.exe");

How to upload a directory via Grails or Java?

What is the best way to upload a directory in grails ?
I try this code :
def upload = {
if(request.method == 'POST') {
Iterator itr = request.getFileNames();
while(itr.hasNext()) {
MultipartFile file = request.getFile(itr.next());
File destination = new File(file.getOriginalFilename())
if (!file.isEmpty()) {
file.transferTo(destination)
// success
}
else
{
// failure
}
}
response.sendError(200,'Done');
}
}
Unfortunately, I can only upload file by file.
I would like to define my directory, and upload all files directly.
Any ideas ?
There is one major misconception here. The code which you posted will only work if both the server and the client runs at physically the same machine (which won't occur in real world) and if you're using the MSIE browser which has the misbehaviour to send the full path along the filename.
You should in fact get the contents of the uploaded file as an InputStream and write it to any OutputStream the usual Java IO way. The filename can be used to create a file with the same name at the server side, but you'll ensure that you strip the incorrectly by MSIE sent path from the filename.
As to your actual functional requirement, HTML doesn't provide facilities to upload complete directories or multiple files by a single <input type="file"> element. You'll need to create a client application which is capable of this and serve this from your webpage, like a Java Applet using Swing JFileChooser. There exist 3rd party solutions for this, like JumpLoader.

Categories