java ee 7 - How to use async servlet + non blocking IO for file download? -
my image files stored in database (i know shouldn't be, can't help). able render them on clients, i've implemented async servlet helps read binary stream off database column , write on output stream of servlet response. traditional io works fine here.
when thought of trying non blocking io async servlet (to test performance), binary data returned in response keeps getting corrupted.
starting oracle blog, i've seen various examples of file upload async nio servlet, no issue.
here's servlet code:
@webservlet(asyncsupported = true, urlpatterns = "/mydownloadservlet") public class fileretrievalservletasyncnio extends httpservlet { private static final long serialversionuid = -6914766655133758332l; @override protected void service(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { queue<byte[]> containerqueue = new linkedlist<byte[]>(); asynccontext asynccontext = request.startasync(); asynccontext.addlistener(new asynclistenerimpl()); asynccontext.settimeout(120000); try { long attachmentid = long.valueof(request.getparameter("id")); myattachmentdataobject retobj = servletutils.fetchattachmentheaders(attachmentid); response = (httpservletresponse) asynccontext.getresponse(); response.setheader("content-length", string.valueof(retobj.getcontentlength())); if (boolean.valueof(request.getparameter(servletconstants.req_param_enable_download))) response.setheader("content-disposition", "attachment; filename=" + retobj.getname()); response.setcontenttype(retobj.getcontenttype()); servletoutputstream sos = response.getoutputstream(); servletutils.fetchcontentstreaminchunks(attachmentid, containerqueue); // reads database , adds queue in chunks sos.setwritelistener(new writelistenerimpl(sos, containerqueue, asynccontext)); } catch (numberformatexception | ioexception exc) { exc.printstacktrace(); request.setattribute("message", "failed"); } } }
here's write listener implementation
public class writelistenerimpl implements writelistener { private servletoutputstream output = null; private queue<byte[]> queue = null; private asynccontext asynccontext = null; private httpservletrequest request = null; private httpservletresponse response = null; public writelistenerimpl(servletoutputstream sos, queue<byte[]> q, asynccontext actx) { output = sos; queue = q; asynccontext = actx; request = (httpservletrequest) asynccontext.getrequest(); } @override public void onwritepossible() throws ioexception { while(output.isready()) { while (!queue.isempty()) { byte[] temp = queue.poll(); output.write(temp, 0, temp.length); } asynccontext.complete(); request.setattribute("message", "success"); } } @override public void onerror(throwable t) { system.err.println(t); try { response.senderror(httpservletresponse.sc_internal_server_error); } catch (ioexception exc) { exc.printstacktrace(); } request.setattribute("message", "failure"); asynccontext.complete(); } }
the response data looks this:
what doing wrong?
not sure expect output in terms of async i/o should check output.isready() before every write. onwritepossible code should be:
while(output.isready() && !queue.isempty()) { byte[] temp = queue.poll(); output.write(temp, 0, temp.length); } if (queue.isempty()) { asynccontext.complete(); request.setattribute("message", "success"); }
this allows onwritepossible() return if writing becomes blocked point of async i/o.
if write when writing blocked (output.isready() return false) different implementations may either ignore write or throw exception. either way output data either missing writes in middle or truncated.
Comments
Post a Comment