Handling missing file download links without request on the same page - java

What's the best practice to handle clicks on file download links when the respective files are missing?
The specific situation is that attachment entities exist in the DB, pointing to file names only and the file storage path can be configured individually/separately. This is for a legacy app and has to be supported.
Is this possible? How does such code look like? I tried
if ( file.canRead() )
{
byte[] data = FileUtils.readFileToByteArray( file );
// goes to code like here: http://balusc.blogspot.de/2006/05/pdf-handling.html
downloadFile( attachment.getFileName(), data );
}
else
{
this.log.errorv( "Attachment {0} not found in configured storage path {1}", file, this.storagePath );
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.addMessage( null,
new FacesMessage( FacesMessage.SEVERITY_ERROR, "Failed.",
"Downlosding file " + file + " failed! File doesn't exist in the configured storage path " + this.storagePath ) );
// ???
facesContext.responseComplete();
}
but this results in
XML-Verarbeitungsfehler: Kein Element gefunden
Adresse: https://localhost:8181/cmc-compliance/view/prototype/collisionManager.xhtml
Zeile Nr. 1, Spalte 1:
(<rant>OMG, I HATE... eh DISLIKE int18ned error messages... someone should get rid of the person who thought this is a good idea... </rant>)
OK, the above means something like "XML processing error: no element found + Line 1, Column 1"
My code is obviously not the right way to do it...
I'm using the JSF code in a datatable:
<h:commandLink action="#{attachmentManager.downloadAttachment(att)}">
<h:graphicImage library="images/icons" name="page_white.png" />
<h:outputText value="#{att.fileName}" />
</h:commandLink>
What I'd ideally want is to display a JSF message (or PrimeFaces growl) and then leave the page as is, that is without issuing a full request on the same page again.
How do you do this?

With facesContext.responseComplete(); you're basically preventing JSF from rendering the response. Hence the client retrieves a completely empty response. The webbrowser is trying to make its best out of it. All the webbrowser knows is that the requested resource has a .xhtml file extension. So the webbrowser presumes that it's some XML content. Then the webbrowser tries to parse the empty response as XML. But it failed miserably with the given error as there are no XML elements at all. A XML well formed document is namely required to have at least one root XML element.
Remove the facesContext.responseComplete(); line and just return from the method the usual way. You should only use facesContext.responseComplete(); when you've already written to the response yourself, such as providing a file download in this particular case.
OMG, I HATE int18ned error messages... someone should get rid of the person who thought this is a good idea...
Just change the operating system platform default locale accordingly in platform specific settings. In case of Windows, a related answer with a screenshot can be found here: Getting OS language in java. If the program itself (e.g. Firefox/Chrome) has also some locale-related settings as well, you might want to change it in the program itself as well.

Related

java.net.URL.getContent UnknownServiceException: no content-type

Please help me to figure out the problem:
We have 2 servers(old and new). And on the old server code works good, but on the new server this code doesn't work:
XWPFDocument doc;
try {
doc = new XWPFDocument((InputStream) this.getClass().getClassLoader().getResource(filePath).getContent());
} catch (IOException e) {
e.printStackTrace();
}
From logs:
java.net.UnknownServiceException: no content-type
at java.net.URLConnection.getContentHandler(URLConnection.java:1241)
at java.net.URLConnection.getContent(URLConnection.java:740)
at java.net.URL.getContent(URL.java:1081)
The File path leads to docx file which is stored inside resources of one the modules of application.
(And of course I've checked the existence of file.)
So the UrlResource is
UrlResource:jar:file:/tomcat/webapps/ourApplication/WEB-INF/lib/moduleName.jar!/internalResourseFolderStructure/file.docx
About servers:
They have a little bit different Tomcat versions:
old - Tomcat 9.0.19.0
new - Tomcat 9.0.33.0
Maybe you can get me some tips how to debug it?
(I am totally sure that it is something with server(Tomcat settings or something), but I don't know on this step how can debug such)
From URLConnection:
getContent
This method first determines the content type of the object by calling the getContentType method. If this is the first time that the application has seen that specific content type, a content handler for that content type is created.
As it's a file URL there are no HTTP headers, which is why the specific error is "no content-type". Even if the content type was available, Java by default has no idea how to turn a application/vnd.openxmlformats-officedocument.wordprocessingml.document into a Java object.
Perhaps on the old server Tomcat was set up with a MIME mapping for .docx and a ContentHandler registered to return something useful.
If what you actually want is an InputStream, then use getResourceAsStream, which will always work.

Blank PDF while downloading

I am facing a very strange issue, I am trying to send the PDF file as attachment from my struts application using below code,
JasperReport jrReport = (JasperReport) JRLoader.loadObject(jasperReport);
JasperPrint jasperPrint = JasperFillManager.fillReport(jrReport, parameters, dataSource);
jasperPrint.setName(fileNameTobeGivenToExportedReport);
response.reset();
response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileNameTobeGivenToExportedReport + ".pdf" + "\"");
response.setHeader("Cache-Control", "private");
JasperExportManager.exportReportToPdfStream(jasperPrint, response.getOutputStream());
but the PDF that is being downloaded is coming with no data, means it is showing the blank page.
When in the above code I added the below line to save the PDF file in my D: directory
File pdf = new File("D:\\sample22.pdf");
JasperExportManager.exportReportToPdfStream(jasperPrint, new FileOutputStream(pdf));
The file that is getting generated is proper, mean with all the data. One thing that I noticed that the file that is downloading from browser and "sample22.pdf" have same size.
I read an article that says that it might be an issue with server configuration as our server might be corrupting the output stream. This is the article that I read Creating PDF from Servlet.
This article says
This can happen when your server flattens all bytes with a value higher than 127. Consult your web (or application) server manual to find out how to make sure binary data is sent correctly to the browser.
I am using struts 1.x, jBoss6, iReport 1.2
Suppose that you have a simple "Hello World" PDF document:
When you open this document, you see that the file structure uses ASCII characters, but that the actual content of the page is compressed to a binary stream:
You don't see the words "Hello World" anywhere, they are compressed along with the PDF syntax that contains info needed to draw these words on the page into this stream:
xœ+är
á26S°00SIá2PÐ5´ 1ôÝBÒ¸4<RsròÂó‹rR5C²€j#*\C¸¹ Çq°
Now suppose that a process shave all the non-ASCII characters into ASCII. I've done this manually as you can see in the next screen shot:
I can still open the document, because I didn't change anything to the file structure: there is still a /Pages three with a single /Page dictionary. From the syntactical point of view, the file looks OK, so I can open it in Adobe Reader:
As you can see, the words "Hello World" are gone. The stream containing the syntax to render these words were corrupted (in my case manually, in your case by the server, or by Struts, or by whatever process you are using that thinks you are creating plain text instead of a binary file).
What you need to do, is to find the place where this happens. Maybe Struts is the culprit. Maybe you are (unintentionally) using Struts as if you were creating a plain text file. It is hard to tell remotely. This is a typical problem caused by a configuration issue. Only somebody with access to your configuration can solve this.

jQuery validation in JSP

For my servlet Java program, I have used JSP and used jQuery client side validation for .xls for uploading a file. It's running successfully for all the user but one of our users is getting problems while uploading a .xls file. The program is showing an alert message and does not allow to upload a file. We also checked browser compatibility.
Questions:
What would be the reason?
Is it adding extra char with extension .xls? If yes, then what would be solution?
Is it a network issue? If yes, then what would be solution?
This is the code:
function dosearch(){
var reportFile = document.form1.reportFile.value;
if(reportFile==null || reportFile==''){
$("#span_filename").show();
if(!window.console)
{
window.console = {log: function(){}};
}else{
console.log("file name is not valid"+reportFile);
}
}else if(reportFile.substring(reportFile.indexOf(".")+1)!="xls"){
alert("Please make sure the uploaded file is an excel file");
if(!window.console)
{
window.console = {log: function(){}};
}else{
console.log("invalid file format or might be its adding extra name or extension name with file , "+reportFile);
}
}else if(confirm('Do you want to upload now?')){
form1.bt1.disabled=true;
$("#pro").show();
document.form1.action='catalog?module=PayrollResultUpload&cmd=doUpload';
document.form1.submit();
}
Well, seems that you are only validating xls extension. To improve this, for example to check xlsx extension, I suggest you to make a regex pattern check.
Change the line:
}else if(reportFile.substring(reportFile.indexOf(".")+1)!="xls"){
with this other version:
}else if(reportFile.substring(reportFile.indexOf(".")+1).match(/^xls.?$/i)){
This will check if there is a character at the end of the extension.
Regarding network issues... You can check the connection with an AJAX worker polling connection regularly and advising the user if there is no connection, but I think its too complicated for this case. Better tell the user to try to upload later ;)
I have done some RND test in browser console there, I have got the exact problem , its getting extra dot(.) before the file but that dot(.) was not in the file it was in the client systems folder structure i.e. C:\ads.test\file\FILE_NAME_HERE.xls . So, I have used lastIndexOf() method instead of indexOf() method . now it running fine without any problem.
#Thiamath , gratitude for your valuable information for me,and be in a part for this question.

Stopping a Servlet from returning a Response

Had a look through SO and couldn't find a question similar to what I'm after. I'll start off by explaining what I'm trying to do, then finish up with a more specific question..
My aim
I have a link that passes a query string parameter to my servlet. That parameter is report. If report = true in the servlet, then I'll generate a PDF document. The PDF document then returns this value, by setting the response's mime type to application/pdf. This code is shown below:
String mimeType = "application/pdf";
res.setContentType(mimeType);
res.setHeader("Content-Disposition", "attachment; filename=\"" +
getEventID(doc) + ".pdf\"");
// Set the response content type and pdf attachment.
out = new ByteArrayOutputStream();
// All PDF Data is pushed onto the output stream.
com.lowagie.text.Document pdfDoc = buildPDF(getEventID(doc));
This code is then written to the response object's output stream.
if(pdfDoc == null)
{
// Something went wrong in generating the report.
return false;
}
// Create the PDF document.
out.writeTo(res.getOutputStream());
If all goes well, the class returns true. If not, it returns false. Now, the problem I'm having is if it returns false. Essentially, I want to point blank stop the data from going anywhere. I added the check to make sure things went well, before I write anything to the output stream, so at the moment what I have is a response that is set to PDF type, but contains no data, if something goes wrong that is.
Next, I have a function that will test the output of the class. If it's true, then all is good, but if it is false, then it sets an error parameter:
if(!PdfReportGenerator.generateReport(res, repositoryURI)) {
req.getSession().setAttribute(SDRestServlet.PDF_ERROR, "error");
// This will then re-direct back to the current URL, meaning the page
// looks like it doesn't do anything.
res.sendRedirect(req.getRequestURI());
}
The problem is, this re-directing is really not helping at all. It's messing up other values that are stored in the request and, while it's making the page appear like it's doing nothing, it doesn't allow me to output an error message to the user.
The issue
While I know how to make it seem like the web response is not returning, it means that I can not output any meaningful information to the user, which is obviously not the ideal outcome.
My question
Is there a way to force the servlet to stop, or return something so that the browser ignores the data?
My second question is, if there is something I can send back to the browser, is there anything I can do on the client side to cause a message to pop up (can be as simple as alert())?
I've been as clear as I possibly can be, so if there's anything you need to know, just ask :)
Is there a way to force the servlet to stop, or return something so
that the browser ignores the data?
Please try setting zero response using method "ServletResponse.setContentLength(int)"
My second question is, if there is something I can send back to the
browser, is there anything I can do on the client side to cause a
message to pop up (can be as simple as alert())?
Yes you can but you need to update back header to say "text/html" and set all the variable as you would do in a normal scenario of a server request
SECOND APPROACH:
If I have to build it from scratch, would following following approach:
First make and AJAX call to find whether pdf need to be generated or not
If response is false show error message.
If response is true send request to server to generate PDF
Hopefully I was able to help you a bit here.

Java: Upload file and get back string (contents) of file

Hi I have GWT client with standard server-side Servlets.
I can upload file from GWT client-side and read it's contents at server-side
I can send it back to client as String
BUT
I have GWT FormPanel with action (myModule+"import"). FormPanel invokes POST from servlet. Browser then redirects me to myurl/import so I can see contents of uploaded file.
This is not what I wanted though. I'd simply like to have my String back. I added
submitCompleteHandler to my FormPanel, but it doesn't log any results.
I noticed that servlets have method such setContentType so I tried text/html, text/plain ... I don't know what should be there ...
To say it in one sentence, I want to send String back to client from servlet without having browser to redirect me somewhere else. Is it possible?
Since you are submitting a form you get your browser to change navigation. In order to make it work the way you want you have to send the file with ajax. For GWT there is the GWTUpload library that allows you to do that.
If the browser redirects you, it's because you gave a "target" to the FormPanel. By default, it submits within an hidden iframe (a.k.a "ajax upload").
As said in the javadoc, you have to setContentType("text/html") in your servlet if you want onSubmitComplete to be reliably called.
onSubmitComplete's results is the returned HTML's body innerHTML so you have to be very careful when sending back values with < or & in them. The only reliable way to get them back is to escape them on the server-side, and unescape them on the client-side. You can either use your own escaping mechanism, or you can use < and &. In the latter case, to unescape on the client-side, you'd either use String#replace, or create an HTML element, set it's innerHTML with the string you got back, and then get its innerText:
public String htmlUnescape(String htmlEscaped) {
Element tmp = Document.get().createDivElement();
tmp.setInnerHTML(htmlEscaped);
return tmp.getInnerText();
}
On the server-side, you'd use:
escaped = content.replace("&", "&").replace("<", "<")
(order matters here if you don't want <s to become &lt;; also, replacing < and & is enough, > and " won't cause any issue here)
In your case however, make sure first that the file's content is "text" and not "binary", as it wouldn't make sense to return it as String could cause issues depending on how you use the value on the client side.

Categories

Resources