📜 ⬆️ ⬇️

Retrieving a file from the server with possible error handling

For one of our intranet systems, we made a simple search through the contents of files attached to official documents. The result of the search was a list of file names and references to the servlet that unloads these files. The servlet reads a file by its identifier from the repository and issues it with a “Content-Type: application / octet-stream” or MIME type corresponding to the file. But what to do if an error occurred on the server, how to tell the operator about it? It would be possible to arrange the redirection to the page with the message, but this is inconvenient - it is necessary to go back, where the data entered into the forms is lost.
On the other hand, you can call the servlet through AJAX XmlHttpRequest and display an error message, but how can you return the file? The callback functions of the XHR object do not work with the binary data received from the server and will not be able to display the standard “Save / Load File” browser browser dialog.

Out of the situation in this way. The client calls the servlet twice. In step 1, he asks him to download the file from the server storage (using AJAX technology passing all the necessary parameters for this), the servlet reads the file and puts its contents, name, MIME type and other attributes into the session, and responds to the client (response format JSON ) either by some session_id (the document was successfully received and waiting for the client), or by an error string that Javascript on the client can easily show via window.alert() . Having received session_id in step 1, the client makes the second move: using the usual type of redirection example.com/servletname?session_id=123456 example.com/servletname?session_id=123456 immediately makes the following request to the same servlet with this parameter and receives in reply the Content-Type: application/octet-stream associates, which ultimately leads to the appearance of a standard browser dialog. After that, the document is removed from the session, freeing up space.

A few short notes:


Additional job description can be obtained in the presented code.
')
AbstractGetFileAJAXWay.java

Abstract Java class that performs the main work. The concrete class-inherited must implement in it two methods that are unique for each case.

package unchqua.getfileajaxway;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.util.Random;

/**
* .
*
* <p> :</p>
* <dl>
* <dt>populateIdentity(HttpServletRequest)</dt>
* <dd> ,
* .</dd>
* <dt>getDocument(HttpServletRequest, Object)</dt>
* <dd> .</dd>
* </dl>
*
* <p>
* : EJB (
* ),
* AbstractGetFileAJAXWay#DSID (,
* ) .
* </p>
*
* <p> JSON- :</p>
* <ul>
* <li> EJB:<br/>
* <pre>{"result":"success","dsid":"362547383846347775"}</pre>
* </li>
* <li> EJB:<br/>
* <pre>{"result":"failure","reason":" !"}</pre>
* </li>
* </ul>
*/
public abstract class AbstractGetFileAJAXWay extends HttpServlet {

public static final String DSID = "dsid";

public class GetFileAJAXWayException extends Exception {
public GetFileAJAXWayException() { super(); }
public GetFileAJAXWayException(String msg) { super(msg); }
public GetFileAJAXWayException(Throwable thw) { super(thw); }
public GetFileAJAXWayException(String msg, Throwable thw) { super(msg, thw); }
}

public interface IFileContainer extends Serializable {
public String getFileName();
public String getContentType();
public long getFileLength();
public byte[] getFileContent();
}

/**
* .
*
* @param req HTTP-.
* @param resp HTTP-.
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

// .
String dsid = req.getParameter(DSID);

// .
if (dsid != null && dsid.length() > 0) {
try {
deliverDocument(dsid, req, resp);
} catch (GetFileAJAXWayException e) {
e.printStackTrace();
throw new ServletException(e);
}
}
// .
else if (req.getParameterMap().size() > 0) {
try {
Object identity = populateIdentity(req);
retrieveDocument(identity, req, resp);
} catch (GetFileAJAXWayException e) {
e.printStackTrace();
throw new ServletException(e);
}
}
// .
else {
final String err = " !";
log(err);
sendFailureReply(err, resp);
return;
}
}

/**
* .
* @param identity , . * @param req HTTP-.
* @param resp HTTP-.
* @throws ServletException
* @throws IOException
*/
private void retrieveDocument
(Object identity, HttpServletRequest req, HttpServletResponse resp)
throws IOException {

// .
HttpSession session = req.getSession(false);

// , .
IFileContainer cont;
try {
cont = getDocument(req, identity);
} catch (Exception e) {
final String err = " : "
+ e.getMessage() + "!";
log(err);
sendFailureReply(err, resp);
return;
}

// .
final String dsid = dsid(
new long[]{ cont.hashCode(),
cont.getFileLength(),
session.hashCode() });

// .
session.setAttribute(dsid, cont);

// .
sendSuccessReply(dsid, resp);
}

/**
* .
* @param dsid .
* @param req HTTP-.
* @param resp HTTP-.
* @throws ServletException
* @throws IOException
*/
private void deliverDocument
(String dsid, HttpServletRequest req, HttpServletResponse resp)
throws GetFileAJAXWayException, IOException {

// .
HttpSession session = req.getSession(false);

// ?
Object sessobj = session.getAttribute(dsid);
if (sessobj == null) {
throw new GetFileAJAXWayException(" \"" + DSID + "\" !");
} else if (!(sessobj instanceof IFileContainer)) {
throw new GetFileAJAXWayException(" \"" + DSID + "\" !");
}

// .
session.removeAttribute(dsid);

// .
IFileContainer document = (IFileContainer) sessobj;

// .
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentLength((int) document.getFileLength());
resp.setContentType(document.getContentType());
resp.setHeader("Content-Transfer-Encoding", "binary");
/* // -- IE
String filename = "=?windows-1251?Q?" + new org.apache.commons.codec.net.QuotedPrintableCodec().encode(document.getFileName(), "Cp1251") + "?=";
resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
*/
/* // IE --
String filename = java.net.URLEncoder.encode(document.getFileName(), "Cp1251").replaceAll("\\+", " ");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
*/
/**/ // -
String filename = document.getFileName();
int dotpos = filename.lastIndexOf('.');
if (dotpos > -1)
filename = "file." + filename.substring(dotpos + 1);
else
filename = "file.dat";
resp.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
/**/
OutputStream out = resp.getOutputStream();
out.write(document.getFileContent());
out.flush();
out.close();
}

/**
* , .
*
* @param trashheap .
* <tt>null</tt>, .
* @return .
*/
private String dsid(long[] trashheap) {
long dsid = System.currentTimeMillis();
if (trashheap != null && trashheap.length > 0)
for (int i = 0; i < trashheap.length; i++)
dsid ^= trashheap[i];
return Long.toString(Math.abs(new Random(dsid).nextLong()), 10);
}

/**
* JSON.
* @param subject .
* @return .
*/
private String escapeJSON(String subject) {
if (subject == null || subject.length() == 0)
return "";
return subject.replaceAll("\"", "\\\"")
.replaceAll("\\\\", "\\\\")
.replaceAll("[\n\r]", "\\\\n");
}

/**
* JSON- .
* @param dsid , () .
* @param resp HTTP-.
* @throws ServletException
* @throws IOException
*/
private void sendSuccessReply(String dsid, HttpServletResponse resp)
throws IOException {
String dsidJSON = "{\"result\":\"success\",\"dsid\":\""
+ escapeJSON(dsid) + "\"}";

sendAnyReply(dsidJSON, resp);
}

/**
* JSON- .
* @param reason .
* @param resp HTTP-.
* @throws ServletException
* @throws IOException
*/
private void sendFailureReply(String reason, HttpServletResponse resp)
throws IOException {
String reasonJSON = "{\"result\":\"failure\",\"reason\":\""
+ escapeJSON(reason) + "\"}";

sendAnyReply(reasonJSON, resp);
}

/**
* .
* @param json .
* @param resp HTTP-.
* @throws IOException
*/
private void sendAnyReply(String json, HttpServletResponse resp)
throws IOException {

final byte[] result_bytes = json.getBytes("UTF-8");
final int CHUNK = 1024;
final BufferedOutputStream output = new BufferedOutputStream(
resp.getOutputStream(), CHUNK);

resp.setStatus(HttpServletResponse.SC_OK);
resp.setHeader("Content-Encoding", "UTF-8");
resp.setContentType("text/plain; charset=UTF-8");
resp.setContentLength(result_bytes.length);

int bytes_pos = 0, bytes_chunk = 0;
do {
bytes_chunk = bytes_pos + CHUNK <= result_bytes.length
? CHUNK
: result_bytes.length - bytes_pos;
output.write(result_bytes, bytes_pos, bytes_chunk);
bytes_pos += bytes_chunk;
} while (bytes_pos < result_bytes.length);
output.flush();
output.close();
}

/**
* .
* @param req HTTP-.
* @return , {@link #getDocument(Object)}
* .
* @throws GetFileAJAXWayException
* .
*/
protected abstract Object populateIdentity(HttpServletRequest req)
throws GetFileAJAXWayException;

/**
* ,
* .
* @param req HTTP-.
* @param identity .
* @return .
* @throws GetFileAJAXWayException .
*/
protected abstract IFileContainer getDocument(HttpServletRequest req,
Object identity) throws GetFileAJAXWayException;

}


ConcreteDocumentRetrievalServlet.java

A heir class that implements the logic required for a particular case.

package unchqua.getfileajaxway;

public class ConcreteDocumentRetrievalServlet extends AbstractGetFileAJAXWay {

public ConcreteDocumentRetrievalServlet() {
super();
}

public Object populateIdentity(HttpServletRequest req)
throws GetFileAJAXWayException {
// .
return null;
}

public IFileContainer getDocument(HttpServletRequest req, Object identity)
throws GetFileAJAXWayException {
// .
return null;
}
}


GetFileAJAXWay.jsp

Sample JSP file that interacts with a servlet.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">
<head>
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8"/>
<meta http-equiv="Expires" content="Tue, Feb 07 1978 15:30:00 GMT"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<meta http-equiv="Content-Script-Type" content="text/javascript"/>
<title>GetFileAJAXWay example</title>

<!-- MochiKit. -->
<script type="text/javascript" src="/MochiKit-1.3.1/Base.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/Iter.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/DOM.js"></script>
<script type="text/javascript" src="/MochiKit-1.3.1/Async.js"></script>

<script type="text/javascript">
<!--

// .
var SERVLET_PATH = "/servletname"; // unchqua.ConcreteDocumentRetrievalServlet .
// URL .
var SERVLET_URL = document.location.protocol + '//'
+ document.location.hostname
+ (document.location.port > 0 ? ':' + document.location.port : '')
+ SERVLET_PATH;

/**
* AJAX-.
*/
function JS_AJAX_GetElFAFile(reqid) {

// .
var parameters = {};
parameters["reqid"] = reqid; // reqid – .
parameters["rand"] = new Date().getTime(); // .

// .
loadJSONDoc(SERVLET_URL, parameters)
.addCallbacks(
JS_AJAX_GetElFAFile_Success,
JS_AJAX_GetElFAFile_Failure);
}

/**
* AJAX- .
*/
function JS_AJAX_GetElFAFile_Success(jsondata) {

// ?
if (JS_AJAX_GetElFAFile_Is_response_error(jsondata)) {
JS_AJAX_GetElFAFile_Failure(jsondata);
return;
}
else if (typeof(jsondata.dsid) == "undefined") {
JS_AJAX_GetElFAFile_Failure("Document is not received!");
return;
}

// .
window.location.href = SERVLET_URL + "?dsid=" + jsondata.dsid;

}

/**
* AJAX- .
*/
function JS_AJAX_GetElFAFile_Failure(jsondata) {

var error_text =
(typeof(jsondata.result) != "undefined"
&amp;&amp; jsondata.result == "failure"
&amp;&amp; typeof(jsondata.reason) != "undefined"
&amp;&amp; jsondata.reason.length > 0)
? jsondata.reason
: jsondata.message + " (" + jsondata.number + ")";

window.alert(error_text);

}

/**
* Is response error?
*
* jsonadata: JSON object just received.
*
* Returns flag (true/false).
*/
function JS_AJAX_GetElFAFile_Is_response_error(jsondata) {

// ( ).
var artifical_error = typeof(jsondata.result) != "undefined"
&amp;&amp; jsondata.result == "failure";

// Internal server error.
var hard_error = typeof(jsondata.number) != "undefined"
&amp;&amp; typeof(jsondata.message) != "undefined"
&amp;&amp; jsondata.number == 500;

return artifical_error || hard_error;

}

//-->
</script>
</head>
<body>

<a href="javascript:JS_AJAX_GetElFAFile(/*docid=*/123);"> !</a>

</body>
</html>

Source: https://habr.com/ru/post/66772/


All Articles