I wrote a simple server using java socket programming and intended to make that offered 2 files for download and display some html response when the download finished. What I did is use PrintWriter.print or DataOutPutStream.writeBytes to send the string including html tags and response string to the browser, then use OutputStream.write to send the file requested. The URL I typed in the browser was like 127.0.0.1/test1.zip, relevant code fragments as following:
pout.print("<html>");
pout.print("<head>");
pout.print("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1/\">");
pout.print("<title>Response</title>");
pout.print("</head>");
pout.print("<body>");
pout.print(createResponseHeader(200, fileTypeCode));
pout.print("</body>");
pout.print("</html>");
pout.print(createResponseHeader(200, fileTypeCode));
pout.flush();
byte[] buffer = new byte[client.getSendBufferSize()];
int bytesRead = 0;
System.out.println("Sending...");
while((bytesRead = requestedFile.read(buffer))>-1)
{
out.write(buffer,0,bytesRead);
}
The pout is a PrintWriter while out is OutputStream.
The problem is when I try to use 127.0.0.1/test2.zip to download the file, it doesn't let me download, instead, print out the response string and a lot of non-sense character in the browser, e.g.
HTTP/1.0 200 OK
Connection: close
Server: COMP5116 Assignment Server v0
Content-Type: application/x-zip-compressed
PK‹â:Lmá^ЛàÍ test2.wmvì[y<”Ûÿ?3ÃØ—Ab¸eeË’5K"»±f_B*à Å*YÛ•¥M5h±¯u[(\·(-÷F)ß3ÏɽݺÝ×ýýñ{Íg^ÏûyžóYÏçœçyÎç¼P’>™îÝ+½Žö6A€;;ýmüH»êt©k]R#*€.G‰µÅRÏøÍLÔóZ; ´£åÑvP¹æª#õó”æÇ„‹&‡ëî9q‰Ú>LkÇÈyÖ2qãÌÆ(ãDŸã©ïÍš]Ð4iIJ0Àª3]B€ðÀ¸CôÁ`ä è1ü½¤Ã¬$ pBi
I believe it simply display the zip file as string with the response header all together. It seems once the PrintWriter is used before the code of sending the file, the whole output stream is used for sending string instead of bytes. However, if I put the part of code of sending the response AFTER the code of sending file, the download works properly but no any response message print out in the browser, just a blank page.
You've to remove your HTML code from here and send only the binary data. You can't mix them in a single servlet.
To achieve what you want to do is not easy.
I would start the download with some JavaScript code in the page, then the page will poll with Ajax for a server side servlet that will know if the download is completed for that particular session. In fact there is no download completed event in JavaScript.
To have this information the download servlet will update the session with a flag when download is completed.
When your Ajax call will return that the download is completed, you can change the text in the page or redirect to a new page.
Edit: Alternatively, if you can change your requirements, it will be much easier to show all messages that you have to show just before the download, and put target="_blank" in the download link so your page is not lost by clicking on the link.
Related
I want to redirect to a page after writing the excel file. The servlet code is given below:
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
workbook.write(outByteStream);
byte [] outArray = outByteStream.toByteArray();
response.setContentType("application/ms-excel");
response.setContentLength(outArray.length);
response.setHeader("Content-Disposition", "attachment; filename=name_"+date+".xlsx");
response.setIntHeader("Refresh", 1);
OutputStream outStream = response.getOutputStream();
outStream.write(outArray);
response.sendRedirect("url/reports.jsp");
This code downloads an Excel file which i have created.
when i call the above servlet, the excel file is being downloaded but it is throwing following exception in the last line :
Servlet Error: ::java.lang.IllegalStateException: Cannot call sendRedirect() after the response has been committed
Hence i am unable to redirect to a new page. what can i do to access the response object after i write the output in "outStream"
The basic problem is that this ...
I want to redirect to a page after writing the excel file.
... describes two separate responses. The server cannot chain them together by itself because the client will expect only one response to each request. Because two requests are required to elicit two responses, automation of this sequence will require client-side scripting.
Personally, I would probably put the script on the front end: a handler on the appropriate button or link that first downloads the file and then (on success) issues a request for the new page. It would also be possible to do as suggested in comments, however: put script in the new page that downloads the file.
You cannot have a body with a redirect because the browser, when receiving a redirect, will issue a second request to the URL it has found (in header Location), and it's the response of that second request that is displayed, unless it is also a redirect, in which case, it will issue a third request, and so on...
I'm in process of making a Server to display a HTML page as a college assessment. All the files are stored locally. Using Firefox to connect to server (chrome seems to block images).
The code below works fine if i type a HTTP Response in the HTML file itself that's being transferred (I'm typing 'HTTP/1.1 200 OK' at start of HTML file)
{
byte[] pageToBytes = Files.readAllBytes(webContent.toPath());
os.write(pageToBytes);
os.flush();
os.close();
}
But if i try and send HTTP response first ,then HTML after, it refuses to load the images in my specified in my HTML code.
Here is Code i'm trying to figure out problem with:
{
byte[] pageToBytes = Files.readAllBytes(webContent.toPath());
String HttpOK = "HTTP/1.1 200 OK\n\r";
os.write(HttpOK.getBytes());
os.write(pageToBytes);
os.flush();
os.close();
}
Any insights would be much appreciated :)
You should read about HTTP requests, when the browser makes a request open a channel of communication between the server and the client, which is the stream you are writing to, this channel closes once the client has received a response.
In your code you are responding once, but the second time the stream is already closed that's why the response body is never reaching the client. Also the server automatically sends a 200 code when there is no error or the code says otherwise.
Since you are trying to make an http server it is good to look at here
it explains how to handle an http request & response.
I am working with a web framework (uPortal) that is handling errors by just throwing an exception and then hanging. The framework works by rendering XML into HTML. When there is an exception, the browser recieves rendered content up to the XML template element that is failing, and then the browser just sits and waits for a timeout. Our team's theory is that the content is sent before the error occurs, which surprised me. Other frameworks I've worked with seem to finish rendering before sending content.
My question is, is there a way to redirect the browser after content has already been sent? In this case, we are in the middle of rendering the content of a <script> tag, but the error could occur potentially anywhere in the html.
My only current thought is to inject some javascript at the top of the page, and to try to change the framework's behavior to fail quickly and close the connection and add </body> and </html> tags when an error occurs. Then the above mentioned javascript would run on pageload and detect if the entire page's content was there and do a client-side redirect if not. Maybe it could look for a special hidden div at the bottom of the page.
Are there any examples of frameworks solving this problem differently or of people using similar framework working around this issue?
You must either capture the error, or capture the output in a buffer. If you can handle the exception, you can probably print a simple script tag like
<script> window.location.href = 'some_new_url';</script>
If the browser understands the doctype to be something related to HTML, it will execute that tag.
If you can capture the output in a buffer, when you handle the error you can decide to send an HTTP redirect to the browser and destroy the output buffer up to that point.
As for other frameowrks, in PHP, you can simply enable output buffering with ob_start(), which won't start sending content until the request is fully completed.
I don't know that framework, but
In http, every response has a response-code associated with it. Since the page is already half-way transferred / rendered that status code (usually "200") was sent (and received) already.
There's no way for the browser to accept another response code (like "301" for redirect) for the same response! Also the server is not able to send another response code, because the original response code was already commited and sent to the client.
Your description of the error and knowledge of the http-protocol implies that there is probably some implementation error in the framework / server components used, OR it was done deliberatly, risking the situation that you are in now...
to redirect a page , you need to set redirect information in header. but you can write header once you start writing content ( may be header is already received by client by the time you compete writing whole document )
But, you can do it in different way as below
1.let document loading complete and record if you need to redirect the page while rendering
2. add a unique request-id identifier for each page load
3. invoke ajax call with request-id ( may be rest call) to server asking if page needs to be redirected.
4. if page needs to be redirected , do so, via javascript in browser at client end.
A HTTP response consists of headers and an optional response content.
Once you have started to write the response to the socket connection you can't revert it. In your example: If you run into an error in the middle of content generation you can't add a redirect header - the header section has already be written.
The statement above is not entirely true: in HTTP chunked transfer encoding the response is sent in separate chunks. The last chunk can have an optional trailer containing entity-header fields and theoretically a redirect header. But if you can use these mechanism is a different question. For instance a servlet container may use chunked transfer encoding but does not give you an API to set the trailer.
But writing must not start immediately: For instance HttpServletResponse maintains a buffer for the response content. If you set headers and start writing the content only the buffer is filled and you still can reset the response and start all over. But once the buffer overflows the response is written to the connection and the HttpServletResponse is now committed.
Such a mechanism gives you way to deal with errors during content generation which happen when the response is not yet committed: Just reset the response and send an error message instead. You could examine your framework if it supports such an mechanism. But obviously this is not a solution for larger responses.
A second way to avoid errors during content generation is simply to make sure that they can't happen. First gather all your data needed for the response (e.g. making unsafe database calls), then in a second step generate the response - the second now step should not fail (except if you have bugs in your code).
You already mentioned a third way to handle an error, by having the client sanitize the response and take some action it errors are detected (e.g. by including a script in the generated HTML response).
The only reliable way to do this is to create a proxy HttpServletResponse object that caches the response. You'd need to give the uPortal this proxy instead of the actual HttpServletResponse, and only send the output using the real response once the processing completes / send redirect if the processing fails.
It is HTTP protocol design limitation that you cannot send HTTP redirect once output was started.
Other possible ways rely on HTML or Javascript redirects, but since you write that the error may happen at any moment, it would be difficult to print it out in a way that the browsers would reliably interpret it as redirect.
Im using struts2 and also servlets. (Due to a 3rd party ajax thing that ships with servlets).
One of my forms is posting to a servlet. (Name "/exclude/new.srl")
I've set struts2 up to ignore all requests to the "exclude" namespace.
So the request is reaching the servlet just fine.
The servlet does its job, and then goes on to do the following before ending:
response.getOutputStream().print("'OK'");
response.getOutputStream().close();
Now i dont know the 3rd party software in detail, so im not exactly sure, but i think the OK statement tells my 3rd party ajax-solution to close the form and refresh some contents of the page.
This all works fine.
Now however i am trying to add a new bit to this. I would like send a file to the user. In other words, when the form is submitted, everything that used to happen should still happen, but also the user should be asked to save or open a file.
So I have created a struts2 action that will return the file, no problem.
But how can i program the servlet to push this file to the user AND ALSO return response OK?
I do not need to use the struts2 action to push the file, if it can be done from within the servlet that is perfectly acceptable.
Anyone?
You can send the file
// Set the headers.
res.setContentType("application/x-download");
res.setHeader("Content-Disposition", "attachment; filename=" + filename);
// Send the file.
OutputStream out = res.getOutputStream();
File file = new File(yourPath);
response.setContentLength((int) file.length());
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file));
int readBytes = 0;
while ((readBytes = buf.read()) != -1)
stream.write(readBytes);
buf.close();
stream.close();
But how can i program the servlet to push this file to the user AND ALSO return response OK?
This I don't think is possible. The response from a servlet has a tyle, denoted by the content-type. If you set it to a download, then you cannot send a normal response, meaning you cannot send the "OK".
You will probably have to setup your unmentioned 3rd party ajax-solution to forward to the file action/servlet URL on form submission success (ie redirect to file download after form was submitted ok).
I have Java webserver (no standard software ... self written). Everything seems to work fine, but when I try to call a page that contains pictures, those pictures are not displayed. Do I have to send images with the output stream to the client? Am I missing an extra step?
As there is too much code to post it here, here is a little outline what happens or is supposed to happen:
1. client logs in
2. client gets a session id and so on
3. the client is connected with an output stream
4. we built the response with the HTML-Code for a certain 'GET'-request
5. look what the GET-request is all about
6. send html response || file || image (not working yet)
So much for the basic outline ...
It sends css-files and stuff, but I still have a problem with images!
Does anybody have an idea? How can I send images from a server to a browser?
Thanks.
I check requests from the client and responses from the server with charles. It sends the files (like css or js) fine, but doesn't with images: though the status is "200 OK" the transfer-encoding is chunked ... I have no idea what that means!? Does anybody know?
EDIT:
Here is the file-reading code:
try{
File requestedFile = new File( file );
PrintStream out = new PrintStream( this.getHttpExchange().getResponseBody() );
// File wird geschickt:
InputStream in = new FileInputStream( requestedFile );
byte content[] = new byte[(int)requestedFile.length()];
in.read( content );
try{
// some header stuff
out.write( content );
}
catch( Exception e ){
e.printStackTrace();
}
in.close();
if(out!=null){
out.close();
System.out.println( "FILE " + uri + " SEND!" );
}
}
catch ( /*all exceptions*/ ) {
// catch it ...
}
Your browser will send separate GET image.png HTTP 1.1 requests to your server, you should handle these file-gets too. There is no good way to embed and image browser-independent in HTML, only the <img src="data:base64codedimage"> protocol handler is available in some browsers.
As you create your HTML response, you can include the contents of the external js/css files directly between <script></script> and <style></style> tags.
Edit: I advise to use Firebug for further diagnostics.
Are you certain that you send out the correct MIME type for the files?
If you need a tiny OpenSource webserver to be inspired by, then have a look at http://www.acme.com/java/software/Acme.Serve.Serve.html which serves us well for ad-hoc server needs.
Do I have to send those external files
or images with the output stream to
the client?
The client will make separate requests for those files, which your server will have to serve. However, those requests can arrive over the same persisten connection (a.k.a. keepalive). The two most likely reasons for your problem:
The client tries to send multiple requests over a persistent connection (which is the default with HTTP 1.1) and your server is not handling this correctly. The easiest way to avoid this is to send a Connection: close header with the response.
The client tries to open a separate connection and your server isn't handling it correctly.
Edit:
There's a problem with this line:
in.read( content );
This method is not guaranteed to fill the array; it will read an arbitrary number of bytes and return that number. You have to use it in a loop to make sure everything is read. Since you have to do a loop anyway, it's a good idea to use a smaller array as a buffer to avoid keeping the whole file in memory and running into an OutOfMemoryError with large files.
Proabably step #4 is where you are going wrong:
// 4. we built the response with the HTML-Code for a certain 'GET'-request
Some of the requests will be a 'GET /css/styles.css' or 'GET /js/main.js' or 'GET /images/header.jpg'. Make sure you stream those files in those circumstances - try loading those URLs directly.
Images (and css/js files) are requested by the browser as completely separate GET requests to the page, so there's definitely no need to "send those ... with the output stream". So if you're getting pages served up ok, but images aren't being loaded, my first guess would be that you're not setting your response headers appropriately (for example, setting the Content-Type of the response to text/html), so the browser isn't interpreting it as a proper page & therefore not loading the images.
Some other things to try if that doesn't work:
Check if you can access an image directly
Use something like firebug or fiddler to check whether the browser is actually requesting the image/css/js files & that all your request/response headers look ok
Use an existing web server!