mirror of
https://github.com/DSpace/DSpace.git
synced 2025-10-12 20:43:18 +00:00
DS-3651: Small refactoring
This commit is contained in:
@@ -452,7 +452,7 @@ public class BitstreamServiceImpl extends DSpaceObjectServiceImpl<Bitstream> imp
|
|||||||
return bitstreamDAO.getNotReferencedBitstreams(context);
|
return bitstreamDAO.getNotReferencedBitstreams(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastModified(Bitstream bitstream) {
|
public Long getLastModified(Bitstream bitstream) {
|
||||||
return bitstreamStorageService.getLastModified(bitstream);
|
return bitstreamStorageService.getLastModified(bitstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -203,5 +203,5 @@ public interface BitstreamService extends DSpaceObjectService<Bitstream>, DSpace
|
|||||||
|
|
||||||
List<Bitstream> getNotReferencedBitstreams(Context context) throws SQLException;
|
List<Bitstream> getNotReferencedBitstreams(Context context) throws SQLException;
|
||||||
|
|
||||||
public String getLastModified(Bitstream bitstream);
|
public Long getLastModified(Bitstream bitstream);
|
||||||
}
|
}
|
||||||
|
@@ -339,7 +339,7 @@ public class BitstreamStorageServiceImpl implements BitstreamStorageService, Ini
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastModified(Bitstream bitstream) {
|
public Long getLastModified(Bitstream bitstream) {
|
||||||
Map wantedMetadata = new HashMap();
|
Map wantedMetadata = new HashMap();
|
||||||
wantedMetadata.put("modified", null);
|
wantedMetadata.put("modified", null);
|
||||||
try {
|
try {
|
||||||
@@ -347,7 +347,7 @@ public class BitstreamStorageServiceImpl implements BitstreamStorageService, Ini
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
}
|
}
|
||||||
return wantedMetadata.get("modified").toString();
|
return Long.valueOf(wantedMetadata.get("modified").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -7,16 +7,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.storage.bitstore.service;
|
package org.dspace.storage.bitstore.service;
|
||||||
|
|
||||||
import org.dspace.authorize.AuthorizeException;
|
|
||||||
import org.dspace.content.Bitstream;
|
|
||||||
import org.dspace.core.Context;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.dspace.authorize.AuthorizeException;
|
||||||
|
import org.dspace.content.Bitstream;
|
||||||
|
import org.dspace.core.Context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <P>
|
* <P>
|
||||||
* Stores, retrieves and deletes bitstreams.
|
* Stores, retrieves and deletes bitstreams.
|
||||||
@@ -191,6 +191,11 @@ public interface BitstreamStorageService {
|
|||||||
public void migrate(Context context, Integer assetstoreSource, Integer assetstoreDestination, boolean deleteOld, Integer batchCommitSize) throws IOException, SQLException, AuthorizeException;
|
public void migrate(Context context, Integer assetstoreSource, Integer assetstoreDestination, boolean deleteOld, Integer batchCommitSize) throws IOException, SQLException, AuthorizeException;
|
||||||
|
|
||||||
|
|
||||||
public String getLastModified(Bitstream bitstream);
|
/**
|
||||||
|
* Get the last modified timestamp of the file linked to the given bitstream
|
||||||
|
* @param bitstream The bitstream for which to get the last modified timestamp
|
||||||
|
* @return The last modified timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
public Long getLastModified(Bitstream bitstream);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,15 +9,19 @@ package org.dspace.app.rest;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.sql.SQLException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.log4j.Logger;
|
||||||
import org.dspace.app.rest.model.BitstreamRest;
|
import org.dspace.app.rest.model.BitstreamRest;
|
||||||
import org.dspace.app.rest.repository.BitstreamRestRepository;
|
import org.dspace.app.rest.utils.ContextUtil;
|
||||||
import org.dspace.app.rest.utils.MultipartFileSender;
|
import org.dspace.app.rest.utils.MultipartFileSender;
|
||||||
|
import org.dspace.content.Bitstream;
|
||||||
|
import org.dspace.content.service.BitstreamService;
|
||||||
|
import org.dspace.core.Context;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -26,47 +30,54 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a specialized controller to provide access to the bitstream binary content
|
* This is a specialized controller to provide access to the bitstream binary content
|
||||||
*
|
|
||||||
* @author Andrea Bollini (andrea.bollini at 4science.it)
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/"+BitstreamRest.CATEGORY +"/"+ BitstreamRest.PLURAL_NAME + "/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}/content")
|
@RequestMapping("/api/"+BitstreamRest.CATEGORY +"/"+ BitstreamRest.PLURAL_NAME + "/{uuid:[0-9a-fxA-FX]{8}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{4}-[0-9a-fxA-FX]{12}}/content")
|
||||||
public class BitstreamContentRestController {
|
public class BitstreamContentRestController {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(BitstreamContentRestController.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BitstreamRestRepository bitstreamRestRepository;
|
private BitstreamService bitstreamService;
|
||||||
|
|
||||||
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD})
|
@RequestMapping(method = {RequestMethod.GET, RequestMethod.HEAD})
|
||||||
public void retrieve(@PathVariable UUID uuid, HttpServletResponse response,
|
public void retrieve(@PathVariable UUID uuid, HttpServletResponse response,
|
||||||
HttpServletRequest request) throws IOException {
|
HttpServletRequest request) throws IOException, SQLException {
|
||||||
BitstreamRest bit = bitstreamRestRepository.findOne(uuid);
|
|
||||||
|
Context context = ContextUtil.obtainContext(request);
|
||||||
|
|
||||||
|
Bitstream bit = bitstreamService.find(context, uuid);
|
||||||
if (bit == null) {
|
if (bit == null) {
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipe the bits
|
Long lastModified = bitstreamService.getLastModified(bit);
|
||||||
InputStream is = bitstreamRestRepository.retrieve(uuid);
|
String mimetype = bit.getFormat(context).getMIMEType();
|
||||||
|
|
||||||
long lastModified = bitstreamRestRepository.getLastModified(uuid);
|
|
||||||
|
|
||||||
String mimetype = bit.getFormat().getMimetype();
|
|
||||||
|
|
||||||
//TODO LOG DOWNLOAD if no range or if last chunk
|
//TODO LOG DOWNLOAD if no range or if last chunk
|
||||||
|
|
||||||
|
// Pipe the bits
|
||||||
|
try(InputStream is = bitstreamService.retrieve(context, bit)) {
|
||||||
|
|
||||||
|
MultipartFileSender
|
||||||
|
.fromInputStream(is)
|
||||||
|
.withFileName(bit.getName())
|
||||||
|
.withLength(bit.getSize())
|
||||||
|
.withChecksum(bit.getChecksum())
|
||||||
|
.withMimetype(mimetype)
|
||||||
|
.withLastModified(lastModified)
|
||||||
|
.with(request)
|
||||||
|
.with(response)
|
||||||
|
|
||||||
|
.serveResource();
|
||||||
|
|
||||||
//MultipartFileSender
|
|
||||||
try {
|
|
||||||
MultipartFileSender.fromBitstream(bit).with(request).with(response).withInputStream(is).withMimetype(mimetype).withLastModified(lastModified).serveResource();
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
log.error(e.getMessage(), e);
|
||||||
} finally {
|
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
|
||||||
IOUtils.closeQuietly(is);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -7,6 +7,14 @@
|
|||||||
*/
|
*/
|
||||||
package org.dspace.app.rest.repository;
|
package org.dspace.app.rest.repository;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.dspace.app.rest.converter.BitstreamConverter;
|
import org.dspace.app.rest.converter.BitstreamConverter;
|
||||||
import org.dspace.app.rest.model.BitstreamRest;
|
import org.dspace.app.rest.model.BitstreamRest;
|
||||||
import org.dspace.app.rest.model.hateoas.BitstreamResource;
|
import org.dspace.app.rest.model.hateoas.BitstreamResource;
|
||||||
@@ -20,14 +28,6 @@ import org.springframework.data.domain.PageImpl;
|
|||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the repository responsible to manage Bitstream Rest object
|
* This is the repository responsible to manage Bitstream Rest object
|
||||||
*
|
*
|
||||||
@@ -111,14 +111,4 @@ public class BitstreamRestRepository extends DSpaceRestRepository<BitstreamRest,
|
|||||||
return is;
|
return is;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLastModified(UUID id) {
|
|
||||||
Bitstream bit = null;
|
|
||||||
Context context = obtainContext();
|
|
||||||
try {
|
|
||||||
bit = bs.find(context, id);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new RuntimeException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return Long.valueOf(bs.getLastModified(bit));
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,15 +1,8 @@
|
|||||||
package org.dspace.app.rest.utils;
|
package org.dspace.app.rest.utils;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import static java.util.Objects.isNull;
|
||||||
import org.dspace.app.rest.model.BitstreamRest;
|
import static java.util.Objects.nonNull;
|
||||||
import org.dspace.services.ConfigurationService;
|
|
||||||
import org.dspace.services.factory.DSpaceServicesFactory;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@@ -17,8 +10,15 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.util.Objects.isNull;
|
import javax.servlet.ServletOutputStream;
|
||||||
import static java.util.Objects.nonNull;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.dspace.services.ConfigurationService;
|
||||||
|
import org.dspace.services.factory.DSpaceServicesFactory;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -60,17 +60,21 @@ public class MultipartFileSender {
|
|||||||
private static final long DEFAULT_EXPIRE_TIME = 60L * 60L * 1000L;
|
private static final long DEFAULT_EXPIRE_TIME = 60L * 60L * 1000L;
|
||||||
|
|
||||||
//no-cache so request is always performed for logging
|
//no-cache so request is always performed for logging
|
||||||
private static final String CACHE_CONTROL_SETTING = "private, no-cache";
|
private static final String CACHE_CONTROL_SETTING = "private,no-cache";
|
||||||
|
|
||||||
BitstreamRest bitstream;
|
private InputStream inputStream;
|
||||||
InputStream inputStream;
|
private HttpServletRequest request;
|
||||||
HttpServletRequest request;
|
private HttpServletResponse response;
|
||||||
HttpServletResponse response;
|
private String contentType;
|
||||||
String contentType;
|
private String disposition;
|
||||||
String disposition = CONTENT_DISPOSITION_INLINE;
|
private long lastModified;
|
||||||
long lastModified;
|
private long length;
|
||||||
|
private String fileName;
|
||||||
|
private String checksum;
|
||||||
|
|
||||||
public MultipartFileSender() {
|
public MultipartFileSender(final InputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
|
||||||
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
ConfigurationService configurationService = DSpaceServicesFactory.getInstance().getConfigurationService();
|
||||||
String bufferSize = configurationService.getProperty("bitstream-download.buffer.size");
|
String bufferSize = configurationService.getProperty("bitstream-download.buffer.size");
|
||||||
if (StringUtils.isNotEmpty(bufferSize)) {
|
if (StringUtils.isNotEmpty(bufferSize)) {
|
||||||
@@ -87,14 +91,9 @@ public class MultipartFileSender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MultipartFileSender fromBitstream(BitstreamRest bitstream) {
|
|
||||||
return new MultipartFileSender().setBitstream(bitstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
//** internal setter **//
|
public static MultipartFileSender fromInputStream(InputStream inputStream) {
|
||||||
private MultipartFileSender setBitstream(BitstreamRest bitstream) {
|
return new MultipartFileSender(inputStream);
|
||||||
this.bitstream = bitstream;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultipartFileSender with(HttpServletRequest httpRequest) {
|
public MultipartFileSender with(HttpServletRequest httpRequest) {
|
||||||
@@ -107,8 +106,18 @@ public class MultipartFileSender {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultipartFileSender withInputStream(InputStream inputStream) {
|
public MultipartFileSender withLength(long length) {
|
||||||
this.inputStream = inputStream;
|
this.length = length;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartFileSender withFileName(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipartFileSender withChecksum(String checksum) {
|
||||||
|
this.checksum = checksum;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,14 +138,11 @@ public class MultipartFileSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inputStream == null) {
|
if (inputStream == null) {
|
||||||
log.error("Bitstream has no content");
|
log.error("Input stream has no content");
|
||||||
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
response.sendError(HttpServletResponse.SC_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Long length = bitstream.getSizeBytes();
|
|
||||||
String fileName = bitstream.getName();
|
|
||||||
|
|
||||||
if (StringUtils.isEmpty(fileName)) {
|
if (StringUtils.isEmpty(fileName)) {
|
||||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||||
return;
|
return;
|
||||||
@@ -145,9 +151,9 @@ public class MultipartFileSender {
|
|||||||
// Validate request headers for caching ---------------------------------------------------
|
// Validate request headers for caching ---------------------------------------------------
|
||||||
// If-None-Match header should contain "*" or ETag. If so, then return 304.
|
// If-None-Match header should contain "*" or ETag. If so, then return 304.
|
||||||
String ifNoneMatch = request.getHeader(IF_NONE_MATCH);
|
String ifNoneMatch = request.getHeader(IF_NONE_MATCH);
|
||||||
if (nonNull(ifNoneMatch) && matches(ifNoneMatch, bitstream.getCheckSum().getValue())) {
|
if (nonNull(ifNoneMatch) && matches(ifNoneMatch, checksum)) {
|
||||||
log.error("If-None-Match header should contain \"*\" or ETag. If so, then return 304.");
|
log.debug("If-None-Match header should contain \"*\" or ETag. If so, then return 304.");
|
||||||
response.setHeader(ETAG, bitstream.getCheckSum().getValue()); // Required in 304.
|
response.setHeader(ETAG, checksum); // Required in 304.
|
||||||
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -156,8 +162,8 @@ public class MultipartFileSender {
|
|||||||
// This header is ignored if any If-None-Match header is specified.
|
// This header is ignored if any If-None-Match header is specified.
|
||||||
long ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE);
|
long ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE);
|
||||||
if (isNull(ifNoneMatch) && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
|
if (isNull(ifNoneMatch) && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
|
||||||
log.error("If-Modified-Since header should be greater than LastModified. If so, then return 304.");
|
log.debug("If-Modified-Since header should be greater than LastModified. If so, then return 304.");
|
||||||
response.setHeader(ETAG, bitstream.getCheckSum().getValue()); // Required in 304.
|
response.setHeader(ETAG, checksum); // Required in 304.
|
||||||
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -201,18 +207,26 @@ public class MultipartFileSender {
|
|||||||
String ifRange = request.getHeader(IF_RANGE);
|
String ifRange = request.getHeader(IF_RANGE);
|
||||||
if (nonNull(ifRange) && !ifRange.equals(fileName)) {
|
if (nonNull(ifRange) && !ifRange.equals(fileName)) {
|
||||||
try {
|
try {
|
||||||
|
//Assume that the If-Range contains a date
|
||||||
long ifRangeTime = request.getDateHeader(IF_RANGE); // Throws IAE if invalid.
|
long ifRangeTime = request.getDateHeader(IF_RANGE); // Throws IAE if invalid.
|
||||||
if (ifRangeTime != -1) {
|
|
||||||
|
if (ifRangeTime == -1 || ifUnmodifiedSince + 1000 <= lastModified) {
|
||||||
|
//Our file has been updated, send the full range
|
||||||
ranges.add(full);
|
ranges.add(full);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IllegalArgumentException ignore) {
|
} catch (IllegalArgumentException ignore) {
|
||||||
ranges.add(full);
|
//Assume that the If-Range contains an ETag
|
||||||
|
if (!matches(ifRange, checksum)) {
|
||||||
|
//Our file has been updated, send the full range
|
||||||
|
ranges.add(full);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any valid If-Range header, then process each part of byte range.
|
// If any valid If-Range header, then process each part of byte range.
|
||||||
if (ranges.isEmpty()) {
|
if (ranges.isEmpty()) {
|
||||||
log.info("If any valid If-Range header, then process each part of byte range.");
|
log.debug("If any valid If-Range header, then process each part of byte range.");
|
||||||
for (String part : range.substring(6).split(",")) {
|
for (String part : range.substring(6).split(",")) {
|
||||||
// Assuming a file with length of 100, the following examples returns bytes at:
|
// 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).
|
// 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
|
||||||
@@ -228,7 +242,7 @@ public class MultipartFileSender {
|
|||||||
|
|
||||||
// Check if Range is syntactically valid. If not, then return 416.
|
// Check if Range is syntactically valid. If not, then return 416.
|
||||||
if (start > end) {
|
if (start > end) {
|
||||||
log.info("Check if Range is syntactically valid. If not, then return 416.");
|
log.warn("Check if Range is syntactically valid. If not, then return 416.");
|
||||||
response.setHeader(CONTENT_RANGE, String.format(BYTES_DINVALID_BYTE_RANGE_FORMAT, length)); // Required in 416.
|
response.setHeader(CONTENT_RANGE, String.format(BYTES_DINVALID_BYTE_RANGE_FORMAT, length)); // Required in 416.
|
||||||
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||||
return;
|
return;
|
||||||
@@ -246,7 +260,7 @@ public class MultipartFileSender {
|
|||||||
response.setBufferSize(DEFAULT_BUFFER_SIZE);
|
response.setBufferSize(DEFAULT_BUFFER_SIZE);
|
||||||
response.setHeader(CONTENT_TYPE, contentType);
|
response.setHeader(CONTENT_TYPE, contentType);
|
||||||
response.setHeader(ACCEPT_RANGES, BYTES);
|
response.setHeader(ACCEPT_RANGES, BYTES);
|
||||||
response.setHeader(ETAG, bitstream.getCheckSum().getValue());
|
response.setHeader(ETAG, checksum);
|
||||||
response.setDateHeader(LAST_MODIFIED, lastModified);
|
response.setDateHeader(LAST_MODIFIED, lastModified);
|
||||||
response.setDateHeader(EXPIRES, System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);
|
response.setDateHeader(EXPIRES, System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);
|
||||||
|
|
||||||
@@ -266,7 +280,6 @@ public class MultipartFileSender {
|
|||||||
log.debug("Content-Disposition : {}", disposition);
|
log.debug("Content-Disposition : {}", disposition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Content phase
|
// Content phase
|
||||||
if (METHOD_HEAD.equals(request.getMethod())) {
|
if (METHOD_HEAD.equals(request.getMethod())) {
|
||||||
log.debug("HEAD request - skipping content");
|
log.debug("HEAD request - skipping content");
|
||||||
@@ -275,31 +288,30 @@ public class MultipartFileSender {
|
|||||||
// Send requested file (part(s)) to client ------------------------------------------------
|
// Send requested file (part(s)) to client ------------------------------------------------
|
||||||
|
|
||||||
// Prepare streams.
|
// Prepare streams.
|
||||||
try (InputStream input = inputStream;
|
try (OutputStream output = response.getOutputStream()) {
|
||||||
OutputStream output = response.getOutputStream()) {
|
|
||||||
|
|
||||||
|
|
||||||
if (ranges.isEmpty() || ranges.get(0) == full) {
|
if (ranges.isEmpty() || ranges.get(0) == full) {
|
||||||
|
|
||||||
// Return full file.
|
// Return full file.
|
||||||
log.info("Return full file");
|
log.debug("Return full file");
|
||||||
response.setContentType(contentType);
|
response.setContentType(contentType);
|
||||||
response.setHeader(CONTENT_RANGE, String.format(BYTES_RANGE_FORMAT, full.start, full.end, full.total));
|
response.setHeader(CONTENT_RANGE, String.format(BYTES_RANGE_FORMAT, full.start, full.end, full.total));
|
||||||
response.setHeader(CONTENT_LENGTH, String.valueOf(full.length));
|
response.setHeader(CONTENT_LENGTH, String.valueOf(full.length));
|
||||||
Range.copy(input, output, length, full.start, full.length);
|
Range.copy(inputStream, output, length, full.start, full.length);
|
||||||
|
|
||||||
} else if (ranges.size() == 1) {
|
} else if (ranges.size() == 1) {
|
||||||
|
|
||||||
// Return single part of file.
|
// Return single part of file.
|
||||||
Range r = ranges.get(0);
|
Range r = ranges.get(0);
|
||||||
log.info("Return 1 part of file : from ({}) to ({})", r.start, r.end);
|
log.debug("Return 1 part of file : from ({}) to ({})", r.start, r.end);
|
||||||
response.setContentType(contentType);
|
response.setContentType(contentType);
|
||||||
response.setHeader(CONTENT_RANGE, String.format(BYTES_RANGE_FORMAT, r.start, r.end, r.total));
|
response.setHeader(CONTENT_RANGE, String.format(BYTES_RANGE_FORMAT, r.start, r.end, r.total));
|
||||||
response.setHeader(CONTENT_LENGTH, String.valueOf(r.length));
|
response.setHeader(CONTENT_LENGTH, String.valueOf(r.length));
|
||||||
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
|
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.
|
||||||
|
|
||||||
// Copy single part range.
|
// Copy single part range.
|
||||||
Range.copy(input, output, length, r.start, r.length);
|
Range.copy(inputStream, output, length, r.start, r.length);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -312,7 +324,7 @@ public class MultipartFileSender {
|
|||||||
|
|
||||||
// Copy multi part range.
|
// Copy multi part range.
|
||||||
for (Range r : Range.relativize(ranges)) {
|
for (Range r : Range.relativize(ranges)) {
|
||||||
log.info("Return multi part of file : from ({}) to ({})", r.start, r.end);
|
log.debug("Return multi part of file : from ({}) to ({})", r.start, r.end);
|
||||||
// Add multipart boundary and header fields for every range.
|
// Add multipart boundary and header fields for every range.
|
||||||
sos.println();
|
sos.println();
|
||||||
sos.println("--" + MULTIPART_BOUNDARY);
|
sos.println("--" + MULTIPART_BOUNDARY);
|
||||||
@@ -320,7 +332,7 @@ public class MultipartFileSender {
|
|||||||
sos.println(CONTENT_RANGE + ": " + String.format(BYTES_RANGE_FORMAT, r.start, r.end, r.total));
|
sos.println(CONTENT_RANGE + ": " + String.format(BYTES_RANGE_FORMAT, r.start, r.end, r.total));
|
||||||
|
|
||||||
// Copy single part range of multi part range.
|
// Copy single part range of multi part range.
|
||||||
Range.copy(input, output, length, r.start, r.length);
|
Range.copy(inputStream, output, length, r.start, r.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// End with multipart boundary.
|
// End with multipart boundary.
|
||||||
@@ -352,12 +364,8 @@ public class MultipartFileSender {
|
|||||||
*/
|
*/
|
||||||
public Range(long start, long end, long total) {
|
public Range(long start, long end, long total) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
// if (end <= start + DEFAULT_BUFFER_SIZE) {
|
this.end = Math.min(end, Math.min(start + DEFAULT_BUFFER_SIZE, total - 1));
|
||||||
this.end = end;
|
this.length = this.end - this.start + 1;
|
||||||
// } else {
|
|
||||||
// this.end = Math.min(start + DEFAULT_BUFFER_SIZE, total - 1);
|
|
||||||
// }
|
|
||||||
this.length = this.end - start + 1;
|
|
||||||
this.total = total;
|
this.total = total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user