/* Copyright c 2005-2012.
* Licensed under GNU LESSER General Public License, Version 3.
* http://www.gnu.org/licenses
*/
package org.beangle.web.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.StopWatch;
import org.beangle.web.mime.MimeTypeProvider;
/**
* SplitStreamDownloader
* <p>
* Split download senario like this:
* <li>Server first response:200</li>
*
* <pre>
* Content-Length=106786028
* Accept-Ranges=bytes
* </pre>
*
* <li>Client send request :</li>
*
* <pre>
* Range: bytes=2000070-106786027
* </pre>
*
* <li>Server send next response:206</li>
*
* <pre>
* Content-Length=106786028
* Content-Range=bytes 2000070-106786027/106786028
* </pre>
*
* @author chaostone
*/
public class SplitStreamDownloader extends DefaultStreamDownloader {
public SplitStreamDownloader() {
super();
}
public SplitStreamDownloader(MimeTypeProvider mimeTypeProvider) {
super(mimeTypeProvider);
}
@Override
public void download(HttpServletRequest request, HttpServletResponse response, InputStream input,
String name, String display) {
String attach = getAttachName(name, display);
response.reset();
addContent(request, response, attach);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("connection", "Keep-Alive");
int length = 0;
long start = 0L;
long begin = 0L;
long stop = 0L;
StopWatch watch = new StopWatch();
watch.start();
try {
length = input.available();
stop = length - 1;
response.setContentLength(length);
String rangestr = request.getHeader("Range");
if (null != rangestr) {
String[] readlength = StringUtils.substringAfter(rangestr, "bytes=").split("-");
start = Long.parseLong(readlength[0]);
if (readlength.length > 1 && StringUtils.isNotEmpty(readlength[1])) {
stop = Long.parseLong(readlength[1]);
}
if (start != 0) {
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
String crange = "bytes " + start + "-" + stop + "/" + length;
response.setHeader("Content-Range", crange);
}
}
OutputStream output = response.getOutputStream();
input.skip(start);
begin = start;
int size = 4 * 1024;
byte[] buffer = new byte[size];
for (int step = maxStep(start, stop, size); step > 0; step = maxStep(start, stop, size)) {
int readed = input.read(buffer, 0, step);
if (readed == -1) break;
output.write(buffer, 0, readed);
start += readed;
}
} catch (IOException e) {
} catch (Exception e) {
logger.warn("download file error " + attach, e);
} finally {
IOUtils.closeQuietly(input);
if (logger.isDebugEnabled()) {
String percent = null;
if (length == 0) {
percent = "100%";
} else {
percent = ((int) (((start - begin) * 1.0 / length) * 10000)) / 100.0f + "%";
}
long time = watch.getTime();
int rate = 0;
if (start - begin > 0) {
rate = (int) (((start - begin) * 1.0 / time * 1000) / 1024);
}
logger.debug("{}({}-{}/{}) download {}[{}] in {} ms with {} KB/s",
array(attach, begin, stop, length, start - begin, percent, time, rate));
}
}
}
private Object[] array(Object... objects) {
return objects;
}
public int maxStep(long start, long stop, int bufferSize) {
if (stop - start + 1 >= bufferSize) {
return bufferSize;
} else {
return (int) (stop - start + 1);
}
}
}