How to properly handle a cancelled file download? The file in question is a pretty large zip file that takes a long time to build, so the user has plenty of time to hit the Cancel button on his download dialog window.
When it happens, a nasty Exception occurs (see below), which is expected, since the client broke the pipe. But how to clean it up? Where/What's the proper way to catch the Exception so that the tomcat logs are not littered with them?
Thank you.
The download code itself is pretty standard code from the textbook:
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
def getZipFile(params) {
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setHeader("Content-Disposition", "attachment; filename=\"filename.zip\"");
response.contentType = "application/zip"
def zos = new ZipOutputStream(response.outputStream)
zos = functionThatCreatesTheZipFile(zos, params) // this takes some time
zos.close()
}
Exception:
rfpmgr [2013-05-01 10:14:32.337] ERROR: web.errors.GrailsExceptionResolver IOException occurred when processing request: [GET] /rfpManager/report/downloadZipFile
Stacktrace follows:
java.io.IOException
at java.util.zip.ZipOutputStream.writeBytes(ZipOutputStream.java:617)
at java.util.zip.ZipOutputStream.writeCEN(ZipOutputStream.java:501)
at java.util.zip.ZipOutputStream.finish(ZipOutputStream.java:348)
at java.util.zip.DeflaterOutputStream.close(DeflaterOutputStream.java:238)
at java.util.zip.ZipOutputStream.close(ZipOutputStream.java:360)
at gov.usgs.eventManager.ZipService.getZipFile(ZipService.groovy:32)
at gov.usgs.eventManager.ReportController$_closure18.doCall(ReportController.groovy:738)
at gov.usgs.jdt.josso.spring.JOSSOProcessingFilter.doFilter(JOSSOProcessingFilter.java:144)
at gov.usgs.jdt.josso.agent.GenericServletSSOAgentFilter.doFilter(GenericServletSSOAgentFilter.java:431)
at java.lang.Thread.run(Thread.java:722)
rfpmgr [2013-05-01 10:14:32.354] ERROR: web.errors.GrailsExceptionResolver IllegalStateException occurred when processing request: [GET] /rfpManager/report/downloadZipFile
getOutputStream() has already been called for this response. Stacktrace follows:
org.codehaus.groovy.grails.web.pages.exceptions.GroovyPagesException: Error processing GroovyPageView: getOutputStream() has already been called for this response
at gov.usgs.jdt.josso.spring.JOSSOProcessingFilter.doFilter(JOSSOProcessingFilter.java:144)
at gov.usgs.jdt.josso.agent.GenericServletSSOAgentFilter.doFilter(GenericServletSSOAgentFilter.java:431)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at gsp_rfpManager_errorserrors_gsp.run(gsp_rfpManager_errorserrors_gsp.groovy:17)
... 3 more
This seems to work just fine:
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import org.apache.catalina.connector.ClientAbortException
def getZipFile(params) {
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
response.setHeader("Content-Disposition", "attachment; filename=\"filename.zip\"");
response.contentType = "application/zip"
try {
def zos = new ZipOutputStream(response.outputStream)
zos = functionThatCreatesTheZipFile(zos, params) // this takes some time
zos.close()
}
catch (ClientAbortException ex) {
println "user aborted download"
}
}
I can see there:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
This is common for Grails controllers when directly rendering things. Solutions:
render ( file: <InputStream>, contentType: 'image/jpeg')
//or
render ( file: <byte[]>, contentType: 'image/jpeg')
//or
GrailsWebRequest webRequest =
(GrailsWebRequest) RequestContextHolder.currentRequestAttributes()
webRequest.setRenderView(false)
That should help.
Related
I want to upload a txt file to a website using a POST request with HTTPBuilder and multipart/form-data
I've tried running my function and I get a HTTP 200 OK response, but the file doesn't appear on the website anywhere.
private Map fileUpload(String url, File file){
log.debug "doPost: $url body: ${file.getName()}"
FileBody fileBody = new FileBody(file,ContentType.APPLICATION_OCTET_STREAM)
def result = [:]
try {
def authSite = new HTTPBuilder(url)
authSite.auth.basic(user, password)
authSite.request(POST) { req ->
headers.Accept = "application/json, text/javascript, */*; q=0.01"
req.params.setParameter(CoreConnectionPNames.SO_TIMEOUT, 20000)
req.params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 60000)
def mpe = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
mpe.addPart("gxt",fileBody)
req.setEntity(mpe)
response.success = { resp, reader ->
result = reader
}
response.failure = { resp, reader ->
println "My response handler got response: ${resp.statusLine}"
}
}
}
catch (e) {
log.debug("Could not perform POST request on URL $url", e)
throw e
}
return result
}
From debugging this is the status recieved
3695 [main] DEBUG org.apache.http.wire - << "HTTP/1.1 200 OK[\r][\n]"
3695 [main] DEBUG org.apache.http.wire - << "Date: Thu, 10 Jan 2019 07:34:06 GMT[\r][\n]"
Anything I'm doing wrong? I don't get any errors but it just seems like nothing happens.
I don't have anything conclusive, but I suspect there is something invalid with the way you set up the multipart upload.
To help figure this out, below is a standalone, working, multipart upload groovy script using HttpBuilder:
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7.1')
#Grab('org.apache.httpcomponents:httpmime:4.2.1')
import org.apache.http.entity.mime.content.*
import org.apache.http.entity.mime.*
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.Method.POST
fileUpload('https://httpbin.org/post', new File('data.txt'))
Map fileUpload(String url, File file){
println "doPost: $url body: ${file.name}"
def result
try {
new HTTPBuilder(url).request(POST) { req ->
requestContentType = "multipart/form-data"
def content = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE)
content.addPart(file.name, new InputStreamBody(file.newInputStream(), file.name))
req.entity = content
// json might be something else (like a reader)
// depending on the response content type
response.success = { resp, json ->
result = json
println "RESP: ${resp.statusLine}, RESULT: $json"
}
response.failure = { resp, json ->
println "My response handler got response: ${resp.statusLine}"
}
}
} catch (e) {
println "Could not perform POST request on URL $url"
throw e
}
result
}
The script assumes a file data.txt with the data to post in the current directory. The script posts to httpbin.org as a working test endpoint, adjust accordingly to post to your endpoint instead.
Saving the above in test.groovy and executing will yield something like:
~> groovy test.groovy
doPost: https://httpbin.org/post body: data.txt
RESP: HTTP/1.1 200 OK, RESULT: [args:[:], data:, files:[data.txt:{ "foo": "bar" }], form:[:], headers:[Accept:*/*, Connection:close, Content-Type:multipart/form-data; boundary=ZVZuV5HAdPOt2Sv7ZjxuUHjd8sDAzCz9VkTqpJYP, Host:httpbin.org, Transfer-Encoding:chunked], json:null, origin:80.252.172.140, url:https://httpbin.org/post]
(note that first run will take a while as groovy grapes need to download the http-builder dependency tree)
perhaps starting with this working example and working your way back to your code would help you pinpoint whatever is not working in your code.
I don't know what to do anymore. Everything seems correct; input/output.
I generate xml file and send to some service to validate.
The response is:
11:10:34,922 INFO [STDOUT] printing out the input stream
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Response>
<Method name="XML/Release/New" time="2013-04-23T15:10:35.1446238Z">
<ResponseStatus>100</ResponseStatus>
</Method>
</Response>
finished printing out the input stream
11:10:34,922 INFO [STDOUT] got the unmarshaller
11:10:34,925 ERROR [PRNDataAccessUtil] Caught an error: javax.xml.bind.UnmarshalException
- with linked exception: [org.xml.sax.SAXParseException: Premature end of file.] : null
The code:
try {
out = connection.getOutputStream();
ByteArrayOutputStream bos = PRNPostNewsReleaseUtil.createNewsReleaseXml(newsRelease);
bos.writeTo(out);
JAXBContext context = JAXBContext.newInstance(Response.class.getPackage().getName());
in = connection.getInputStream();
BufferedReader inp = new BufferedReader(new InputStreamReader(in));
System.out.println("printing out the input stream");
String line;
while((line = inp.readLine()) != null) {
System.out.println(line);
}
System.out.println("finished printing out the input stream");
Unmarshaller unmarshaller = context.createUnmarshaller();
response = (Response) unmarshaller.unmarshal(in);
} catch (Exception ex) {
log.error("Caught an error: " + ex + " : " + ex.getMessage());
return null;
} finally {
if (null != in) connection.disconnect();
}
You are getting the error because the InputStream has been advanced to the end during the output. Assuming the buffer in your BufferedReader is large enough to contain the whole XML document you can reset it after outputting and then unmarshal that.
One time happened to me that I was using the wrong class name to build the JAXBContext object, so when I tried to marshall an object, an empty XML file was created, thus making the unmarshaller fail.
So make sure the JAXBContext object is instantiated with the class you're trying to marshall.
Another thing to note here is even if you are not reading the buffer explicitly in code but have a expression watch that reads the input, it would end up having the same effect of incrementing the stream head. Figured that out after spending hours on debugging this exception.
I am trying to allow users to download a file, and have been trying for quite a while to no avail to get this to work.
The code I have been using is:
Response.ClearHeaders();
Response.Clear();
Response.BufferOutput = true;
Response.AddHeader("Content-disposition",
"attachment; filename= "+ Path.GetFileName(path) + fileType);
Response.ContentType = "application/octet-stream";
Response.BinaryWrite(buffer);
Response.Flush();
Response.Close();
Response.End();
Unfortunately nothing happens when I try to make it run except for lots of exceptions:
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll
A first chance exception of type 'System.Web.HttpException' occurred in System.Web.Mvc.dll
A first chance exception of type 'System.NullReferenceException' occurred in WebDev.WebHost40.dll
(etc)
Changing my debugging settings I discovered that the exception I was getting was
"Server cannot set content type after HTTP headers have been sent."
I have searched through Google and attempted many solutions but nothing have worked so far. This is the only time Response has been used as far as I know - unless there is some background processes occurring (if so, how do I change this?). Please note that I am using Asp.NET MVC.
Any ideas?
Edit:
Here is some context, which might help to resolve the problem:
The code is inside a Webmethod that has been called via Ajax, the reason being that I needed to call the server side via the client side (bad practice, but it was needed in the time provided) and I also needed to pass through a parameter.
Here is the ajax call:
$("#Button").each(function() {
this.submitting = false; //To prevent double-clicking problems
}).on("click", function (e) {
if (e.preventDefault) e.preventDefault();
$.ajaxSetup({ cache: false });
e.preventDefault();
if (!this.submitting)
{
this.submitting = true;
var self = this;
$.ajax({
url: 'Controller/retrieveFile',
type: 'POST',
data: { SystemNumber: $("#Button").val() },
dataType: 'json',
success: function (data) {
self.submitting = false;
},
error: function() {
self.submitting = false;
}
});
}
});
Is this, perhaps, a problem with the return value? Currently I am returning
Json(true, JsonRequestBehavior.AllowGet);
Try using HttpContext.Response.AppendHeader("Content-disposition", ....) and getting rid of the Response.ClearHeaders(); and Response.Clear(); lines.
Also you should consider using FileContentResult as the return type of your method.
I am always getting the "Error Connecting to ${url}" message?
Can anyone please show me my mistake?
def url = new URL("https://www.google.com")
HttpURLConnection connection = (HttpURLConnection) url.openConnection()
connection.setRequestMethod("GET")
// connection.setConnectTimeout(10000)
connection.connect()
if (connection.responseCode == 200 || connection.responseCode == 201) {
def returnMessage = connection.content
//print out the full response
println returnMessage
} else {
println "Error Connecting to " + url
}
| Error 2012-07-05 00:04:05,950 [http-bio-8080-exec-6] ERROR errors.GrailsExceptionResolver - ConnectException occurred when processing request: [GET] /CopperApplications/urlTracker Connection timed out: connect. Stacktrace follows: Message: Connection timed out: connec
Your code seems correct, and produces the expected(?) results on Groovy 1.7.9 with 1.6.0_33 JVM. There is likely something amiss with your network (as indicated by the connection timeout error).
We have a custom JSF2 Exception Handler ...
Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
boolean isUnHandledException = false;
SystemException se = null;
while(i.hasNext()) {
ExceptionQueuedEvent event = (ExceptionQueuedEvent)i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext)event.getSource();
Throwable t = context.getException();
try {
if (apperror)
take to app error page
if (filenotfound)
take to page not found error page
}catch(){
} finally {
i.remove ().....causes problem....in filenot found...
.....
}
The application exception handling works fine ,without any issues.
But the FileNotFound in our custom handler causes the issue. The exception handler catches the FileNotFound ,but while trying to remove the queuedevent i.remove it results in NullPointerException ,if i comment i.remove it works fine...
java.lang.NullPointerException
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:96)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
This isn't entirely the right place to handle a FileNotFoundException coming from Mojarra. There's then no means of a UIViewRoot. Line 96 in RenderResponsePhase tries to do a facesContext.getViewRoot().getViewId(), but that fails then with that NPE.
Better handle it with a servlet filter, or just with an <error-page> if you have a custom 404 error page.
So, either in the filter which is mapped on the FacesServlet:
try {
chain.doFilter(request, response);
}
catch (FileNotFoundException e) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI());
}
This will then end up in the server's default HTTP 404 error page, or any custom <error-page> with an <error-code> of 404. OmniFaces has also such a filter.
Or in an <error-page> in web.xml matching <exception-type> of FileNotFoundException.
<error-page>
<exception-type>java.io.FileNotFoundException</exception-type>
<location>/WEB-INF/errorpages/404.xhtml</location>
</error-page>