Example usage for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT

List of usage examples for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT

Introduction

In this page you can find the example usage for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT.

Prototype

int SC_PARTIAL_CONTENT

To view the source code for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT.

Click Source Link

Document

Status code (206) indicating that the server has fulfilled the partial GET request for the resource.

Usage

From source file:de.mendelson.comm.as2.send.MessageHttpUploader.java

/**Returns the created header for the sent data*/
public Properties upload(HttpConnectionParameter connectionParameter, AS2Message message, Partner sender,
        Partner receiver) throws Exception {
    NumberFormat formatter = new DecimalFormat("0.00");
    AS2Info as2Info = message.getAS2Info();
    MessageAccessDB messageAccess = null;
    MDNAccessDB mdnAccess = null;//  ww w.  jav a 2  s  .  c o m
    if (this.runtimeConnection != null && messageAccess == null && !as2Info.isMDN()) {
        messageAccess = new MessageAccessDB(this.configConnection, this.runtimeConnection);
        messageAccess.initializeOrUpdateMessage((AS2MessageInfo) as2Info);
    } else if (this.runtimeConnection != null && as2Info.isMDN()) {
        mdnAccess = new MDNAccessDB(this.configConnection, this.runtimeConnection);
        mdnAccess.initializeOrUpdateMDN((AS2MDNInfo) as2Info);
    }
    if (this.clientserver != null) {
        this.clientserver.broadcastToClients(new RefreshClientMessageOverviewList());
    }
    long startTime = System.currentTimeMillis();
    //sets the global requestHeader
    int returnCode = this.performUpload(connectionParameter, message, sender, receiver);
    long size = message.getRawDataSize();
    long transferTime = System.currentTimeMillis() - startTime;
    float bytePerSec = (float) ((float) size * 1000f / (float) transferTime);
    float kbPerSec = (float) (bytePerSec / 1024f);
    if (returnCode == HttpServletResponse.SC_OK) {
        if (this.logger != null) {
            this.logger.log(Level.INFO,
                    this.rb.getResourceString("returncode.ok",
                            new Object[] { as2Info.getMessageId(), String.valueOf(returnCode),
                                    AS2Tools.getDataSizeDisplay(size), AS2Tools.getTimeDisplay(transferTime),
                                    formatter.format(kbPerSec), }),
                    as2Info);
        }
    } else if (returnCode == HttpServletResponse.SC_ACCEPTED || returnCode == HttpServletResponse.SC_CREATED
            || returnCode == HttpServletResponse.SC_NO_CONTENT
            || returnCode == HttpServletResponse.SC_RESET_CONTENT
            || returnCode == HttpServletResponse.SC_PARTIAL_CONTENT) {
        if (this.logger != null) {
            this.logger.log(Level.INFO,
                    this.rb.getResourceString("returncode.accepted",
                            new Object[] { as2Info.getMessageId(), String.valueOf(returnCode),
                                    AS2Tools.getDataSizeDisplay(size), AS2Tools.getTimeDisplay(transferTime),
                                    formatter.format(kbPerSec), }),
                    as2Info);
        }
    } else {
        //the system was unable to connect the partner
        if (returnCode < 0) {
            throw new NoConnectionException(
                    this.rb.getResourceString("error.noconnection", as2Info.getMessageId()));
        }
        if (this.runtimeConnection != null) {
            if (messageAccess == null) {
                messageAccess = new MessageAccessDB(this.configConnection, this.runtimeConnection);
            }
            messageAccess.setMessageState(as2Info.getMessageId(), AS2Message.STATE_STOPPED);
        }
        throw new Exception(as2Info.getMessageId() + ": HTTP " + returnCode);
    }
    if (this.configConnection != null) {
        //inc the sent data size, this is for new connections (as2 messages, async mdn)
        AS2Server.incRawSentData(size);
        if (message.getAS2Info().isMDN()) {
            AS2MDNInfo mdnInfo = (AS2MDNInfo) message.getAS2Info();
            //ASYNC MDN sent: insert an entry into the statistic table
            QuotaAccessDB.incReceivedMessages(this.configConnection, this.runtimeConnection,
                    mdnInfo.getSenderId(), mdnInfo.getReceiverId(), mdnInfo.getState(),
                    mdnInfo.getRelatedMessageId());
        }
    }
    if (this.configConnection != null) {
        MessageStoreHandler messageStoreHandler = new MessageStoreHandler(this.configConnection,
                this.runtimeConnection);
        messageStoreHandler.storeSentMessage(message, sender, receiver, this.requestHeader);
    }
    //inform the server of the result if a sync MDN has been requested
    if (!message.isMDN()) {
        AS2MessageInfo messageInfo = (AS2MessageInfo) message.getAS2Info();
        if (messageInfo.requestsSyncMDN()) {
            //perform a check if the answer really contains a MDN or is just an empty HTTP 200 with some header data
            //this check looks for the existance of some key header values
            boolean as2FromExists = false;
            boolean as2ToExists = false;
            for (int i = 0; i < this.getResponseHeader().length; i++) {
                String key = this.getResponseHeader()[i].getName();
                if (key.toLowerCase().equals("as2-to")) {
                    as2ToExists = true;
                } else if (key.toLowerCase().equals("as2-from")) {
                    as2FromExists = true;
                }
            }
            if (!as2ToExists) {
                throw new Exception(this.rb.getResourceString("answer.no.sync.mdn",
                        new Object[] { as2Info.getMessageId(), "as2-to" }));
            }
            //send the data to the as2 server. It does not care if the MDN has been sync or async anymore
            GenericClient client = new GenericClient();
            CommandObjectIncomingMessage commandObject = new CommandObjectIncomingMessage();
            //create temporary file to store the data
            File tempFile = AS2Tools.createTempFile("SYNCMDN_received", ".bin");
            FileOutputStream outStream = new FileOutputStream(tempFile);
            ByteArrayInputStream memIn = new ByteArrayInputStream(this.responseData);
            this.copyStreams(memIn, outStream);
            memIn.close();
            outStream.flush();
            outStream.close();
            commandObject.setMessageDataFilename(tempFile.getAbsolutePath());
            for (int i = 0; i < this.getResponseHeader().length; i++) {
                String key = this.getResponseHeader()[i].getName();
                String value = this.getResponseHeader()[i].getValue();
                commandObject.addHeader(key.toLowerCase(), value);
                if (key.toLowerCase().equals("content-type")) {
                    commandObject.setContentType(value);
                }
            }
            //compatibility issue: some AS2 systems do not send a as2-from in the sync case, even if
            //this if _NOT_ RFC conform
            //see RFC 4130, section 6.2: The AS2-To and AS2-From header fields MUST be
            //present in all AS2 messages and AS2 MDNs whether asynchronous or synchronous in nature,
            //except for asynchronous MDNs, which are sent using SMTP.
            if (!as2FromExists) {
                commandObject.addHeader("as2-from",
                        AS2Message.escapeFromToHeader(receiver.getAS2Identification()));
            }
            ErrorObject errorObject = client.send(commandObject);
            if (errorObject.getErrors() > 0) {
                messageAccess.setMessageState(as2Info.getMessageId(), AS2Message.STATE_STOPPED);
            }
            tempFile.delete();
        }
    }
    return (this.requestHeader);
}

From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java

@Test
public void test_process_request_multipart_three_ranges_with_spaces() throws Exception {
    final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
    httpServletRequest.setMethod("GET");
    httpServletRequest.setPathInfo("/input.dat");
    httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=0-5, 6-10,     11-20");

    final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
    HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.dat",
            "application/pdf", INPUT_FILE, false);

    assertEquals(readFromFile("response3.dat"), mockHttpServletResponse.getContentAsString());

    assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus());
    assertEquals("multipart/byteranges; boundary=THIS_STRING_SEPARATES",
            mockHttpServletResponse.getContentType());
    assertEquals("attachment;filename=\"input.dat\"",
            mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION));
}

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;//  www  . j a v  a2  s .c o 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.github.zhanhb.ckfinder.download.PathPartial.java

/**
 * Serve the specified resource, optionally including the data content.
 *
 * @param request The servlet request we are processing
 * @param response The servlet response we are creating
 * @param content Should the content be included?
 * @param path the resource to serve/*w  w w. j  a va 2  s .  co  m*/
 *
 * @exception IOException if an input/output error occurs
 */
private void serveResource(HttpServletRequest request, HttpServletResponse response, boolean content, Path path)
        throws IOException, ServletException {
    ActionContext context = new ActionContext().put(HttpServletRequest.class, request)
            .put(HttpServletResponse.class, response).put(ServletContext.class, request.getServletContext())
            .put(Path.class, path);
    if (path == null) {
        notFound.handle(context);
        return;
    }
    BasicFileAttributes attr;
    try {
        attr = Files.readAttributes(path, BasicFileAttributes.class);
    } catch (IOException ex) {
        notFound.handle(context);
        return;
    }
    context.put(BasicFileAttributes.class, attr);

    boolean isError = response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST;
    // Check if the conditions specified in the optional If headers are
    // satisfied.
    // Checking If headers
    boolean included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null);
    String etag = this.eTag.getValue(context);
    if (!included && !isError && !checkIfHeaders(request, response, attr, etag)) {
        return;
    }
    // Find content type.
    String contentType = contentTypeResolver.getValue(context);
    // Get content length
    long contentLength = attr.size();
    // Special case for zero length files, which would cause a
    // (silent) ISE
    boolean serveContent = content && contentLength != 0;
    Range[] ranges = null;
    if (!isError) {
        if (useAcceptRanges) {
            // Accept ranges header
            response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
        }
        // Parse range specifier
        ranges = serveContent ? parseRange(request, response, attr, etag) : FULL;
        // ETag header
        response.setHeader(HttpHeaders.ETAG, etag);
        // Last-Modified header
        response.setDateHeader(HttpHeaders.LAST_MODIFIED, attr.lastModifiedTime().toMillis());
    }
    ServletOutputStream ostream = null;
    if (serveContent) {
        ostream = response.getOutputStream();
    }

    String disposition = contentDisposition.getValue(context);
    if (disposition != null) {
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, disposition);
    }

    // Check to see if a Filter, Valve of wrapper has written some content.
    // If it has, disable range requests and setting of a content length
    // since neither can be done reliably.
    if (isError || ranges == FULL) {
        // Set the appropriate output headers
        if (contentType != null) {
            log.debug("serveFile: contentType='{}'", contentType);
            response.setContentType(contentType);
        }
        if (contentLength >= 0) {
            setContentLengthLong(response, contentLength);
        }
        // Copy the input stream to our output stream (if requested)
        if (serveContent) {
            log.trace("Serving bytes");
            Files.copy(path, ostream);
        }
    } else if (ranges != null && ranges.length != 0) {
        // Partial content response.
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        if (ranges.length == 1) {
            Range range = ranges[0];
            response.addHeader(HttpHeaders.CONTENT_RANGE, range.toString());
            long length = range.end - range.start + 1;
            setContentLengthLong(response, length);
            if (contentType != null) {
                log.debug("serveFile: contentType='{}'", contentType);
                response.setContentType(contentType);
            }
            if (serveContent) {
                try (InputStream stream = Files.newInputStream(path)) {
                    copyRange(stream, ostream, range, new byte[Math.min((int) length, 8192)]);
                }
            }
        } else {
            response.setContentType("multipart/byteranges; boundary=" + MIME_SEPARATION);
            if (serveContent) {
                copy(path, ostream, ranges, contentType, new byte[Math.min((int) contentLength, 8192)]);
            }
        }
    }
}

From source file:org.hsweb.web.controller.file.FileController.java

/**
 * ID?,?//www.  ja va2s.  co  m
 * : http://host:port/file/download/aSk2a/file.zip  IDaSk2a.??file.zip
 *
 * @param id       ??id
 * @param name     ??????.?(null).??
 * @param response {@link HttpServletResponse}
 * @param request  {@link HttpServletRequest}
 * @return , , ?
 * @throws IOException       
 * @throws NotFoundException ?
 */
@RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
@AccessLogger("")
public ResponseMessage downLoad(@PathVariable("id") String id,
        @RequestParam(value = "name", required = false) String name, HttpServletResponse response,
        HttpServletRequest request) throws IOException {
    Resources resources = resourcesService.selectByPk(id);
    if (resources == null || resources.getStatus() != 1) {
        throw new NotFoundException("?");
    } else {
        if (!"file".equals(resources.getType()))
            throw new NotFoundException("?");
        //?contentTypeapplication/octet-stream
        String contentType = mediaTypeMapper.get(resources.getSuffix().toLowerCase());
        if (contentType == null)
            contentType = "application/octet-stream";
        //????
        if (StringUtils.isNullOrEmpty(name))
            name = resources.getName();
        //????
        if (!name.contains("."))
            name = name.concat(".").concat(resources.getSuffix());
        //
        name = fileNameKeyWordPattern.matcher(name).replaceAll("");
        int skip = 0;
        long fSize = resources.getSize();
        //??
        try {
            //???
            String Range = request.getHeader("Range").replaceAll("bytes=", "").replaceAll("-", "");
            skip = StringUtils.toInt(Range);
        } catch (Exception e) {
        }
        response.setContentLength((int) fSize);//?
        response.setContentType(contentType);
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(name, "utf-8"));
        //try with resource
        try (BufferedInputStream inputStream = new BufferedInputStream(fileService.readResources(resources));
                BufferedOutputStream stream = new BufferedOutputStream(response.getOutputStream())) {
            //
            if (skip > 0) {
                inputStream.skip(skip);
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                String contentRange = new StringBuffer("bytes ").append(skip).append("-").append(fSize - 1)
                        .append("/").append(fSize).toString();
                response.setHeader("Content-Range", contentRange);
            }
            byte b[] = new byte[2048 * 10];
            while ((inputStream.read(b)) != -1) {
                stream.write(b);
            }
            stream.flush();
        } catch (IOException e) {
            logger.debug(String.format("download file error%s", e.getMessage()));
            throw e;
        }
        return null;
    }

}

From source file:com.swingtech.apps.filemgmt.controller.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.  jav  a2  s .  c  om*/
    }
    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 && HttpUtils.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 && !HttpUtils.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 = StringUtils.sublong(part, 0, part.indexOf("-"));
                long end = StringUtils.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 && HttpUtils.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);
    }

}

From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java

@Test
public void test_process_request_plain_minus_range() throws Exception {
    final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
    httpServletRequest.setMethod("GET");
    httpServletRequest.setPathInfo("/input.dat");
    httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=-50");

    final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
    HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.dat",
            "application/pdf", INPUT_FILE, false);

    assertEquals("BbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
            mockHttpServletResponse.getContentAsString());

    assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus());
    assertEquals("application/pdf", mockHttpServletResponse.getContentType());
    assertEquals("inline;filename=\"input.dat\"",
            mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION));
    assertEquals("50", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH));

}

From source file:org.jahia.services.content.files.FileServlet.java

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

    long timer = System.currentTimeMillis();
    int code = HttpServletResponse.SC_OK;
    try {//  ww w  .  j a  va 2s.c  om
        FileKey fileKey = parseKey(req);

        if (fileKey != null && fileKey.getWorkspace() != null && StringUtils.isNotEmpty(fileKey.getPath())) {

            Cache<String, FileLastModifiedCacheEntry> lastModifiedCache = cacheManager.getLastModifiedCache();

            FileLastModifiedCacheEntry lastModifiedEntry = lastModifiedCache.get(fileKey.getCacheKey());
            if (isNotModified(fileKey, lastModifiedEntry, req, res)) {
                // resource is not changed
                code = HttpServletResponse.SC_NOT_MODIFIED;
                res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                logAccess(fileKey, req, "ok-not-modified");
                return;
            }

            Cache<String, Map<String, FileCacheEntry>> contentCache = cacheManager.getContentCache();

            Map<String, FileCacheEntry> entries = contentCache.get(fileKey.getCacheKey());
            FileCacheEntry fileEntry = entries != null ? entries.get(fileKey.getThumbnail()) : null;
            if (fileEntry == null) {
                JCRNodeWrapper n = getNode(fileKey);
                if (n == null || !n.isFile()) {
                    // cannot find it or it is not a file
                    code = HttpServletResponse.SC_NOT_FOUND;
                    res.sendError(HttpServletResponse.SC_NOT_FOUND);
                    return;
                }

                Date lastModifiedDate = n.getLastModifiedAsDate();
                long lastModified = lastModifiedDate != null ? lastModifiedDate.getTime() : 0;
                String eTag = generateETag(n.getIdentifier(), lastModified);
                if (lastModifiedEntry == null) {
                    lastModifiedEntry = new FileLastModifiedCacheEntry(eTag, lastModified);
                    if (canCache(n)) {
                        lastModifiedCache.put(fileKey.getCacheKey(), lastModifiedEntry);
                    }
                }

                if (isNotModified(fileKey, lastModifiedEntry, req, res)) {
                    // resource is not changed
                    code = HttpServletResponse.SC_NOT_MODIFIED;
                    res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    logAccess(fileKey, req, "ok-not-modified");
                    return;
                }

                fileEntry = getFileEntry(fileKey, n, lastModifiedEntry);
                if (fileEntry != null && fileEntry.getData() != null) {
                    entries = contentCache.get(fileKey.getCacheKey());
                    if (entries == null) {
                        entries = new HashMap<String, FileCacheEntry>(1);
                    }
                    entries.put(fileKey.getThumbnail(), fileEntry);
                    contentCache.put(fileKey.getCacheKey(), entries);
                    logAccess(fileKey, req, "ok");
                }
            } else {
                if (lastModifiedEntry == null) {
                    lastModifiedEntry = new FileLastModifiedCacheEntry(fileEntry.getETag(),
                            fileEntry.getLastModified());
                    lastModifiedCache.put(fileKey.getCacheKey(), lastModifiedEntry);
                }
                logAccess(fileKey, req, "ok-cached");
                if (logger.isDebugEnabled()) {
                    logger.debug("Serving cached file entry {}", fileKey.toString());
                }
            }

            if (fileEntry != null) {
                List<RangeUtils.Range> ranges;
                boolean useRanges = true;
                if (fileEntry.getBinary() instanceof BinaryRangesSupport) {
                    useRanges = ((BinaryRangesSupport) fileEntry.getBinary()).supportRanges();
                }

                ranges = useRanges ? RangeUtils.parseRange(req, res, fileEntry.getETag(),
                        fileEntry.getLastModified(), fileEntry.getContentLength()) : null;

                if (fileKey.getPath().indexOf('%', fileKey.getPath().lastIndexOf('/')) != -1) {
                    res.setHeader("Content-Disposition",
                            "inline; filename=\"" + JCRContentUtils.unescapeLocalNodeName(
                                    StringUtils.substringAfterLast(fileKey.getPath(), "/")) + "\"");
                }
                res.setDateHeader("Last-Modified", fileEntry.getLastModified());
                res.setHeader("ETag", fileEntry.getETag());
                InputStream is = null;

                if (fileEntry.getData() != null) {
                    // writing in-memory data
                    is = new ByteArrayInputStream(fileEntry.getData());
                } else if (fileEntry.getBinary() != null) {
                    // spool from an input stream
                    is = fileEntry.getBinary().getStream();
                } else {
                    code = HttpServletResponse.SC_NOT_FOUND;
                    res.sendError(HttpServletResponse.SC_NOT_FOUND);
                    return;
                }

                if (ranges == null || (ranges == RangeUtils.FULL)) {
                    res.setContentType(fileEntry.getMimeType());
                    if (fileEntry.getContentLength() <= Integer.MAX_VALUE) {
                        res.setContentLength((int) fileEntry.getContentLength());
                    } else {
                        res.setHeader("Content-Length", Long.toString(fileEntry.getContentLength()));
                    }
                    ServletOutputStream os = res.getOutputStream();
                    IOUtils.copy(is, os);
                    os.flush();
                    os.close();
                } else {
                    res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                    if (ranges.size() == 1) {
                        res.setContentType(fileEntry.getMimeType());
                        RangeUtils.Range range = (RangeUtils.Range) ranges.get(0);
                        res.addHeader("Content-Range",
                                "bytes " + range.start + "-" + range.end + "/" + range.length);
                        long length = range.end - range.start + 1;
                        if (length < Integer.MAX_VALUE) {
                            res.setContentLength((int) length);
                        } else {
                            // Set the content-length as String to be able to use a long
                            res.setHeader("Content-Length", "" + length);
                        }
                        ServletOutputStream os = res.getOutputStream();
                        RangeUtils.copy(is, os, range);
                        IOUtils.closeQuietly(is);
                        IOUtils.closeQuietly(os);

                    } else {
                        res.setContentType("multipart/byteranges; boundary=" + RangeUtils.MIME_SEPARATION);

                        try {
                            res.setBufferSize(RangeUtils.getOutput());
                        } catch (IllegalStateException e) {
                            // Silent catch
                        }
                        ServletOutputStream os = res.getOutputStream();
                        RangeUtils.copy(is, os, ranges.iterator(), fileEntry.getMimeType());
                        IOUtils.closeQuietly(is);
                        IOUtils.closeQuietly(os);
                    }
                }
                if ((fileEntry.getData() == null) && (fileEntry.getBinary() != null)) {
                    fileEntry.getBinary().dispose();
                    fileEntry.setBinary(null);
                }
                SpringContextSingleton.getInstance()
                        .publishEvent(new FileDownloadEvent(this, req, fileEntry.getIdentifier(),
                                fileKey.getPath(), fileEntry.getNodeTypes(), fileKey.getWorkspace()));
            } else {
                code = HttpServletResponse.SC_NOT_FOUND;
                res.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
        } else {
            code = HttpServletResponse.SC_NOT_FOUND;
            res.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    } catch (RepositoryException e) {
        logger.error("Cannot get file", e);

        code = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
        res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    } finally {
        if (logger.isDebugEnabled()) {
            logger.debug("Served [{}] with status code [{}] in [{}ms]",
                    new Object[] {
                            req.getRequestURI()
                                    + (req.getQueryString() != null ? "?" + req.getQueryString() : ""),
                            code, (System.currentTimeMillis() - timer) });
        }
    }
}

From source file:com.sastix.cms.server.utils.MultipartFileSender.java

public void serveResource() throws Exception {
    if (response == null || request == null) {
        return;//from  w  w  w  .j a va2s.  c om
    }

    Long length = Files.size(filepath);
    String fileName = filepath.getFileName().toString();
    FileTime lastModifiedObj = Files.getLastModifiedTime(filepath);

    if (StringUtils.isEmpty(fileName) || lastModifiedObj == null) {
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return;
    }
    long lastModified = LocalDateTime
            .ofInstant(lastModifiedObj.toInstant(), ZoneId.of(ZoneOffset.systemDefault().getId()))
            .toEpochSecond(ZoneOffset.UTC);

    // 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 && HttpUtils.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 && !HttpUtils.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 = Range.sublong(part, 0, part.indexOf("-"));
                long end = Range.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 && HttpUtils.accepts(accept, contentType) ? "inline" : "attachment";
    }
    LOG.debug("Content-Type : {}", contentType);
    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Type", contentType);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    LOG.debug("Content-Disposition : {}", disposition);
    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.
    try (InputStream input = new BufferedInputStream(Files.newInputStream(filepath));
            OutputStream output = response.getOutputStream()) {

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            LOG.debug("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));
            Range.copy(input, output, length, full.start, full.length);

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            LOG.debug("Return 1 part of file : from ({}) to ({})", r.start, r.end);
            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.
            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) {
                LOG.debug("Return multi part of file : from ({}) to ({})", r.start, r.end);
                // 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.
                Range.copy(input, output, length, r.start, r.length);
            }

            // End with multipart boundary.
            sos.println();
            sos.println("--" + MULTIPART_BOUNDARY + "--");
        }
    }

}

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  w  ww. ja va2s. c om*/
    }
    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);
    }

}