List of usage examples for javax.servlet.http HttpServletRequest getDateHeader
public long getDateHeader(String name);
long
value that represents a Date
object. From source file:annis.gui.servlets.ResourceServlet.java
@Override @SuppressWarnings("unchecked") public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { OutputStream outStream = response.getOutputStream(); String completePath = request.getPathInfo(); if (completePath == null) { response.sendError(404, "must provide a valid and existing path with a vistype"); return;/*from w w w . jav a 2 s.c o m*/ } // remove trailing / completePath = completePath.substring(1); String[] pathComponents = completePath.split("/"); String vistype = pathComponents[0]; if (pathComponents.length < 2) { response.sendError(404, "must provide a valid and existing path"); return; } String path = StringUtils.join(Arrays.copyOfRange(pathComponents, 1, pathComponents.length), "/"); // get the visualizer for this vistype ResourcePlugin vis = resourceRegistry.get(vistype); if (vis == null) { response.sendError(500, "There is no resource with the short name " + vistype); } else if (path.endsWith(".class")) { response.sendError(403, "illegal class path access"); } else { URL resource = vis.getClass().getResource(path); if (resource == null) { response.sendError(404, path + " not found"); } else { // check if it is new URLConnection resourceConnection = resource.openConnection(); long resourceLastModified = resourceConnection.getLastModified(); long requestLastModified = request.getDateHeader("If-Modified-Since"); if (requestLastModified != -1 && resourceLastModified <= requestLastModified) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { response.addDateHeader("Last-Modified", resourceLastModified); if ("localhost".equals(request.getServerName())) { // does always expire right now response.addDateHeader("Expires", new Date().getTime()); } else { // expires in one minute per default response.addDateHeader("Expires", new Date().getTime() + 60000); } // not in cache, stream out String mimeType = getServletContext().getMimeType(path); response.setContentType(mimeType); if (mimeType.startsWith("text/")) { response.setCharacterEncoding("UTF-8"); } OutputStream bufferedOut = new BufferedOutputStream(outStream); InputStream resourceInStream = new BufferedInputStream(resource.openStream()); try { int v = -1; while ((v = resourceInStream.read()) != -1) { bufferedOut.write(v); } } finally { resourceInStream.close(); bufferedOut.flush(); outStream.flush(); } } } } }
From source file:org.kurento.repository.internal.http.RepositoryHttpServlet.java
/** * Parse the range header./*from w ww. j a v a2 s . c o m*/ * * @param request * The servlet request we are processing * @param response * The servlet response we are creating * @return Vector of ranges */ protected List<Range> parseRange(HttpServletRequest request, HttpServletResponse response, RepositoryItemAttributes resourceAttributes) throws IOException { // Checking If-Range String headerValue = request.getHeader("If-Range"); if (headerValue != null) { long headerValueTime = -1L; try { headerValueTime = request.getDateHeader("If-Range"); } catch (IllegalArgumentException e) { // Ignore } String eTag = resourceAttributes.getETag(); long lastModified = resourceAttributes.getLastModified(); if (headerValueTime == -1L) { // If the ETag the client gave does not match the entity // etag, then the entire entity is returned. if (!eTag.equals(headerValue.trim())) { return FULL; } } else { // If the timestamp of the entity the client got is older than // the last modification date of the entity, the entire entity // is returned. if (lastModified > headerValueTime + 1000) { return FULL; } } } long fileLength = resourceAttributes.getContentLength(); if (fileLength == 0) { return null; } // Retrieving the range header (if any is specified String rangeHeader = request.getHeader("Range"); if (rangeHeader == null) { return null; } // bytes is the only range unit supported (and I don't see the point // of adding new ones). if (!rangeHeader.startsWith("bytes")) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } rangeHeader = rangeHeader.substring(6); // Vector which will contain all the ranges which are successfully // parsed. List<Range> result = new ArrayList<>(); StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ","); // Parsing the range list while (commaTokenizer.hasMoreTokens()) { String rangeDefinition = commaTokenizer.nextToken().trim(); Range currentRange = new Range(); currentRange.length = fileLength; int dashPos = rangeDefinition.indexOf('-'); if (dashPos == -1) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } if (dashPos == 0) { try { long offset = Long.parseLong(rangeDefinition); currentRange.start = fileLength + offset; currentRange.end = fileLength - 1; } catch (NumberFormatException e) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } } else { try { currentRange.start = Long.parseLong(rangeDefinition.substring(0, dashPos)); if (dashPos < rangeDefinition.length() - 1) { currentRange.end = Long .parseLong(rangeDefinition.substring(dashPos + 1, rangeDefinition.length())); } else { currentRange.end = fileLength - 1; } } catch (NumberFormatException e) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } } if (!currentRange.validate()) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } result.add(currentRange); } return result; }
From source file:com.kurento.kmf.repository.internal.http.RepositoryHttpServlet.java
/** * Parse the range header./*w ww . ja v a 2 s .c o m*/ * * @param request * The servlet request we are processing * @param response * The servlet response we are creating * @return Vector of ranges */ protected List<Range> parseRange(HttpServletRequest request, HttpServletResponse response, RepositoryItemAttributes resourceAttributes) throws IOException { // Checking If-Range String headerValue = request.getHeader("If-Range"); if (headerValue != null) { long headerValueTime = -1L; try { headerValueTime = request.getDateHeader("If-Range"); } catch (IllegalArgumentException e) { // Ignore } String eTag = resourceAttributes.getETag(); long lastModified = resourceAttributes.getLastModified(); if (headerValueTime == -1L) { // If the ETag the client gave does not match the entity // etag, then the entire entity is returned. if (!eTag.equals(headerValue.trim())) { return FULL; } } else { // If the timestamp of the entity the client got is older than // the last modification date of the entity, the entire entity // is returned. if (lastModified > (headerValueTime + 1000)) { return FULL; } } } long fileLength = resourceAttributes.getContentLength(); if (fileLength == 0) { return null; } // Retrieving the range header (if any is specified String rangeHeader = request.getHeader("Range"); if (rangeHeader == null) { return null; } // bytes is the only range unit supported (and I don't see the point // of adding new ones). if (!rangeHeader.startsWith("bytes")) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } rangeHeader = rangeHeader.substring(6); // Vector which will contain all the ranges which are successfully // parsed. List<Range> result = new ArrayList<>(); StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ","); // Parsing the range list while (commaTokenizer.hasMoreTokens()) { String rangeDefinition = commaTokenizer.nextToken().trim(); Range currentRange = new Range(); currentRange.length = fileLength; int dashPos = rangeDefinition.indexOf('-'); if (dashPos == -1) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } if (dashPos == 0) { try { long offset = Long.parseLong(rangeDefinition); currentRange.start = fileLength + offset; currentRange.end = fileLength - 1; } catch (NumberFormatException e) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } } else { try { currentRange.start = Long.parseLong(rangeDefinition.substring(0, dashPos)); if (dashPos < rangeDefinition.length() - 1) { currentRange.end = Long .parseLong(rangeDefinition.substring(dashPos + 1, rangeDefinition.length())); } else { currentRange.end = fileLength - 1; } } catch (NumberFormatException e) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } } if (!currentRange.validate()) { response.addHeader("Content-Range", "bytes */" + fileLength); response.sendError(SC_REQUESTED_RANGE_NOT_SATISFIABLE); return null; } result.add(currentRange); } return result; }
From source file:net.yacy.http.servlets.YaCyDefaultServlet.java
protected boolean passConditionalHeaders(HttpServletRequest request, HttpServletResponse response, Resource resource) throws IOException { try {/*from w ww . j a v a 2 s. c o m*/ if (!request.getMethod().equals(HttpMethod.HEAD.asString())) { String ifms = request.getHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); if (ifms != null) { long ifmsl = request.getDateHeader(HttpHeader.IF_MODIFIED_SINCE.asString()); if (ifmsl != -1) { if (resource.lastModified() / 1000 <= ifmsl / 1000) { response.reset(); response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); response.flushBuffer(); return false; } } } // Parse the if[un]modified dates and compare to resource long date = request.getDateHeader(HttpHeader.IF_UNMODIFIED_SINCE.asString()); if (date != -1) { if (resource.lastModified() / 1000 > date / 1000) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return false; } } } } catch (IllegalArgumentException iae) { if (!response.isCommitted()) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, iae.getMessage()); return false; } throw iae; } return true; }
From source file:org.openbravo.erpCommon.utility.ImageUtils.java
/** * Outputs the image/content to the response *//* ww w. jav a 2 s . co m*/ public static void outputImageResource(final HttpServletRequest req, final HttpServletResponse resp, final String imageType) throws IOException, ServletException { try { OBContext.setAdminMode(true); // enforce cache validation/checks every time resp.addHeader(RESPONSE_HEADER_CACHE_CONTROL, RESPONSE_NO_CACHE); VariablesSecureApp vars = new VariablesSecureApp(req); Image img = null; if (imageType == "logo") { String logo = vars.getStringParameter("logo"); String org = vars.getStringParameter("orgId"); img = Utility.getImageLogoObject(logo, org); if (img == null) { byte[] imageFileContent = Utility.getImageLogo(logo, org); String mimeType = MimeTypeUtil.getInstance().getMimeTypeName(imageFileContent); resp.setContentType(mimeType); OutputStream out = resp.getOutputStream(); resp.setContentLength(imageFileContent.length); out.write(imageFileContent); out.close(); return; } } else { img = Utility.getImageObject(vars.getStringParameter("id")); } if (img != null) { String imageID = "IMGTAG" + img.getUpdated().toString(); if (ImageUtils.isImageResponseRequired(req, resp, imageID)) { // read the image data byte[] imgByte = img.getBindaryData(); // write the mimetype String mimeType = img.getMimetype();// write the mimetype if (mimeType == null) { mimeType = MimeTypeUtil.getInstance().getMimeTypeName(img.getBindaryData()); if (img != null) { // If there is an OBContext, we attempt to save the MIME type of the image updateMimeType(img.getId(), mimeType); } } if (!mimeType.equals("")) { resp.setContentType(mimeType); } // write the image OutputStream out = resp.getOutputStream(); resp.setContentLength(imgByte.length); out.write(imgByte); out.close(); } else { resp.sendError(HttpServletResponse.SC_NOT_MODIFIED); resp.setDateHeader(RESPONSE_HEADER_LASTMODIFIED, req.getDateHeader(REQUEST_HEADER_IFMODIFIEDSINCE)); } } } finally { OBContext.restorePreviousMode(); } }
From source file:net.sourceforge.subsonic.controller.RESTController.java
public ModelAndView download(HttpServletRequest request, HttpServletResponse response) throws Exception { request = wrapRequest(request);//from ww w . j av a2 s .c o m User user = securityService.getCurrentUser(request); if (!user.isDownloadRole()) { error(request, response, ErrorCode.NOT_AUTHORIZED, user.getUsername() + " is not authorized to download files."); return null; } long ifModifiedSince = request.getDateHeader("If-Modified-Since"); long lastModified = downloadController.getLastModified(request); if (ifModifiedSince != -1 && lastModified != -1 && lastModified <= ifModifiedSince) { response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return null; } if (lastModified != -1) { response.setDateHeader("Last-Modified", lastModified); } return downloadController.handleRequest(request, response); }
From source file:org.olat.commons.servlets.StaticsLegacyDispatcher.java
/** * Serve the requested resource.//from www . j a va 2 s . co m * * @param request * @param response * @param copyContent * @return False if serving the resource failed/was aborted. * @throws IOException */ private boolean serveResource(HttpServletRequest request, HttpServletResponse response, boolean copyContent) throws IOException { // just another internal forward or even a direct call String path = getRelativePath(request); if (path.indexOf("/secstatic/") == 0) { path = path.substring(10, path.length()); } PathHandler handler = null; String relPath = null; String handlerName = null; long start = 0; boolean logDebug = log.isDebug(); if (logDebug) start = System.currentTimeMillis(); try { relPath = path.substring(1); int index = relPath.indexOf('/'); if (index != -1) { handlerName = relPath.substring(0, index); relPath = relPath.substring(index); } if (handlerName != null) { handler = StaticsModule.getInstance(handlerName); } } catch (IndexOutOfBoundsException e) { // if some problem with the url, we assign no handler } if (handler == null || relPath == null) { // no handler found or relPath incomplete response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); return false; } ResourceDescriptor rd = handler.getResourceDescriptor(request, relPath); if (rd == null) { // no handler found or relPath incomplete response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); return false; } // check if modified since long ifModifiedSince = request.getDateHeader("If-Modified-Since"); long lastMod = rd.getLastModified(); if (lastMod != -1L && ifModifiedSince >= lastMod) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return false; } // server the resource if (copyContent) { InputStream is = handler.getInputStream(request, rd); if (is == null) { // resource not found or access denied response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI()); return false; } StaticMediaResource smr = new StaticMediaResource(is, rd); ServletUtil.serveResource(request, response, smr); if (logDebug) { long stop = System.currentTimeMillis(); log.debug("Serving resource '" + relPath + "' (" + rd.getSize() + " bytes) in " + (stop - start) + "ms with handler '" + handlerName + "'."); } } return true; }
From source file:org.gaul.s3proxy.S3ProxyHandler.java
private static void handleBlobMetadata(HttpServletRequest request, HttpServletResponse response, BlobStore blobStore, String containerName, String blobName) throws IOException, S3Exception { BlobMetadata metadata = blobStore.blobMetadata(containerName, blobName); if (metadata == null) { throw new S3Exception(S3ErrorCode.NO_SUCH_KEY); }//w w w.j a v a 2 s . c om // BlobStore.blobMetadata does not support GetOptions so we emulate // conditional requests. String ifMatch = request.getHeader(HttpHeaders.IF_MATCH); String ifNoneMatch = request.getHeader(HttpHeaders.IF_NONE_MATCH); long ifModifiedSince = request.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE); long ifUnmodifiedSince = request.getDateHeader(HttpHeaders.IF_UNMODIFIED_SINCE); String eTag = metadata.getETag(); if (eTag != null) { eTag = maybeQuoteETag(eTag); if (ifMatch != null && !ifMatch.equals(eTag)) { throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED); } if (ifNoneMatch != null && ifNoneMatch.equals(eTag)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } } Date lastModified = metadata.getLastModified(); if (lastModified != null) { if (ifModifiedSince != -1 && lastModified.compareTo(new Date(ifModifiedSince)) <= 0) { throw new S3Exception(S3ErrorCode.PRECONDITION_FAILED); } if (ifUnmodifiedSince != -1 && lastModified.compareTo(new Date(ifUnmodifiedSince)) >= 0) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } } response.setStatus(HttpServletResponse.SC_OK); addMetadataToResponse(request, response, metadata); }
From source file:net.dorokhov.pony.web.server.common.StreamingViewRenderer.java
@Override protected void renderMergedOutputModel(Map objectMap, HttpServletRequest request, HttpServletResponse response) throws Exception { InputStream dataStream = (InputStream) objectMap.get(DownloadConstants.INPUT_STREAM); if (dataStream == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return;/*from ww w .j a v a 2s . co m*/ } long length = (Long) objectMap.get(DownloadConstants.CONTENT_LENGTH); String fileName = (String) objectMap.get(DownloadConstants.FILENAME); Date lastModifiedObj = (Date) objectMap.get(DownloadConstants.LAST_MODIFIED); if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } long lastModified = lastModifiedObj.getTime(); String contentType = (String) objectMap.get(DownloadConstants.CONTENT_TYPE); // Validate request headers for caching --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return 304. String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null && matches(ifNoneMatch, fileName)) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // If-Modified-Since header should be greater than LastModified. If so, then return 304. // This header is ignored if any If-None-Match header is specified. long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // Validate request headers for resume ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412. String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !matches(ifMatch, fileName)) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // If-Unmodified-Since header should be greater than LastModified. If not, then return 412. long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // Validate and process range ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file. Range full = new Range(0, length - 1, length); List<Range> ranges = new ArrayList<>(); // Validate and process Range and If-Range headers. String range = request.getHeader("Range"); if (range != null) { // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416. if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } String ifRange = request.getHeader("If-Range"); if (ifRange != null && !ifRange.equals(fileName)) { try { long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid. if (ifRangeTime != -1) { ranges.add(full); } } catch (IllegalArgumentException ignore) { ranges.add(full); } } // If any valid If-Range header, then process each part of byte range. if (ranges.isEmpty()) { for (String part : range.substring(6).split(",")) { // Assuming a file with length of 100, the following examples returns bytes at: // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100). long start = sublong(part, 0, part.indexOf("-")); long end = sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) { start = length - end; end = length - 1; } else if (end == -1 || end > length - 1) { end = length - 1; } // Check if Range is syntactically valid. If not, then return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + length); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } // Add range. ranges.add(new Range(start, end, length)); } } } // Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set content disposition. String disposition = "inline"; // If content type is unknown, then set the default value. // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp // To add new content types, add new mime-mapping entry in web.xml. if (contentType == null) { contentType = "application/octet-stream"; } else if (!contentType.startsWith("image")) { // Else, expect for images, determine content disposition. If content type is supported by // the browser, then set to inline, else attachment which will pop a 'save as' dialogue. String accept = request.getHeader("Accept"); disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; } // Initialize response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", fileName); response.setDateHeader("Last-Modified", lastModified); response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams. InputStream input = null; OutputStream output = null; try { // Open streams. input = new BufferedInputStream(dataStream); output = response.getOutputStream(); if (ranges.isEmpty() || ranges.get(0) == full) { // Return full file. response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + full.start + "-" + full.end + "/" + full.total); response.setHeader("Content-Length", String.valueOf(full.length)); copy(input, output, length, full.start, full.length); } else if (ranges.size() == 1) { // Return single part of file. Range r = ranges.get(0); response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Copy single part range. copy(input, output, length, r.start, r.length); } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Cast back to ServletOutputStream to get the easy println methods. ServletOutputStream sos = (ServletOutputStream) output; // Copy multi part range. for (Range r : ranges) { // Add multipart boundary and header fields for every range. sos.println(); sos.println("--" + MULTIPART_BOUNDARY); sos.println("Content-Type: " + contentType); sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); // Copy single part range of multi part range. copy(input, output, length, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + MULTIPART_BOUNDARY + "--"); } } finally { // Gently close streams. close(output); close(input); close(dataStream); } }
From source file:com.harrywu.springweb.common.StreamingViewRenderer.java
@Override public void renderMergedOutputModel(Map<String, Object> objectMap, HttpServletRequest request, HttpServletResponse response) throws Exception { InputStream dataStream = (InputStream) objectMap.get(INPUT_STREAM); if (dataStream == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return;//from ww w .j a v a 2 s .c o m } long length = (Long) objectMap.get(CONTENT_LENGTH); String fileName = (String) objectMap.get(FILENAME); Date lastModifiedObj = (Date) objectMap.get(LAST_MODIFIED); if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } long lastModified = lastModifiedObj.getTime(); String contentType = (String) objectMap.get(CONTENT_TYPE); // Validate request headers for caching // --------------------------------------------------- // If-None-Match header should contain "*" or ETag. If so, then return // 304. String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null && matches(ifNoneMatch, fileName)) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // If-Modified-Since header should be greater than LastModified. If so, // then return 304. // This header is ignored if any If-None-Match header is specified. long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) { response.setHeader("ETag", fileName); // Required in 304. response.sendError(HttpServletResponse.SC_NOT_MODIFIED); return; } // Validate request headers for resume // ---------------------------------------------------- // If-Match header should contain "*" or ETag. If not, then return 412. String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !matches(ifMatch, fileName)) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // If-Unmodified-Since header should be greater than LastModified. If // not, then return 412. long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) { response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); return; } // Validate and process range // ------------------------------------------------------------- // Prepare some variables. The full Range represents the complete file. Range full = new Range(0, length - 1, length); List<Range> ranges = new ArrayList<Range>(); // Validate and process Range and If-Range headers. String range = request.getHeader("Range"); if (range != null) { // Range header should match format "bytes=n-n,n-n,n-n...". If not, // then return 416. if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { response.setHeader("Content-Range", "bytes */" + length); // Required // in // 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } String ifRange = request.getHeader("If-Range"); if (ifRange != null && !ifRange.equals(fileName)) { try { long ifRangeTime = request.getDateHeader("If-Range"); // Throws // IAE // if // invalid. if (ifRangeTime != -1) { ranges.add(full); } } catch (IllegalArgumentException ignore) { ranges.add(full); } } // If any valid If-Range header, then process each part of byte // range. if (ranges.isEmpty()) { for (String part : range.substring(6).split(",")) { // Assuming a file with length of 100, the following // examples returns bytes at: // 50-80 (50 to 80), 40- (40 to length=100), -20 // (length-20=80 to length=100). long start = sublong(part, 0, part.indexOf("-")); long end = sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) { start = length - end; end = length - 1; } else if (end == -1 || end > length - 1) { end = length - 1; } // Check if Range is syntactically valid. If not, then // return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + length); // Required // in // 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } // Add range. ranges.add(new Range(start, end, length)); } } } // Prepare and initialize response // -------------------------------------------------------- // Get content type by file name and set content disposition. String disposition = "inline"; // If content type is unknown, then set the default value. // For all content types, see: // http://www.w3schools.com/media/media_mimeref.asp // To add new content types, add new mime-mapping entry in web.xml. if (contentType == null) { contentType = "application/octet-stream"; } else if (!contentType.startsWith("image")) { // Else, expect for images, determine content disposition. If // content type is supported by // the browser, then set to inline, else attachment which will pop a // 'save as' dialogue. String accept = request.getHeader("Accept"); disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; } // Initialize response. response.reset(); response.setBufferSize(DEFAULT_BUFFER_SIZE); response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", fileName); response.setDateHeader("Last-Modified", lastModified); response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME); // Send requested file (part(s)) to client // ------------------------------------------------ // Prepare streams. InputStream input = null; OutputStream output = null; try { // Open streams. input = new BufferedInputStream(dataStream); output = response.getOutputStream(); if (ranges.isEmpty() || ranges.get(0) == full) { // Return full file. Range r = full; response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); copy(input, output, length, r.start, r.length); } else if (ranges.size() == 1) { // Return single part of file. Range r = ranges.get(0); response.setContentType(contentType); response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total); response.setHeader("Content-Length", String.valueOf(r.length)); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Copy single part range. copy(input, output, length, r.start, r.length); } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // Cast back to ServletOutputStream to get the easy println // methods. ServletOutputStream sos = (ServletOutputStream) output; // Copy multi part range. for (Range r : ranges) { // Add multipart boundary and header fields for every range. sos.println(); sos.println("--" + MULTIPART_BOUNDARY); sos.println("Content-Type: " + contentType); sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total); // Copy single part range of multi part range. copy(input, output, length, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + MULTIPART_BOUNDARY + "--"); } } finally { // Gently close streams. close(output); close(input); close(dataStream); } }