package play.mvc.results;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.commons.codec.net.URLCodec;
import play.exceptions.UnexpectedException;
import play.libs.MimeTypes;
import play.mvc.Http.Request;
import play.mvc.Http.Response;
/**
* 200 OK with application/octet-stream
*/
public class RenderBinary extends Result {
private static final String INLINE_DISPOSITION_TYPE = "inline";
private static final String ATTACHMENT_DISPOSITION_TYPE = "attachment";
private static URLCodec encoder = new URLCodec();
boolean inline = false;
long length = 0;
File file;
InputStream is;
String name;
String contentType;
/**
* send a binary stream as the response
* @param is the stream to read from
* @param name the name to use as Content-Diposition attachement filename
*/
public RenderBinary(InputStream is, String name) {
this(is, name, false);
}
public RenderBinary(InputStream is, String name, long length) {
this(is, name, length, false);
}
/**
* send a binary stream as the response
* @param is the stream to read from
* @param name the name to use as Content-Diposition attachement filename
* @param inline true to set the response Content-Disposition to inline
*/
public RenderBinary(InputStream is, String name, boolean inline) {
this(is, name, null, inline);
}
/**
* send a binary stream as the response
* @param is the stream to read from
* @param name the name to use as Content-Diposition attachement filename
* @param inline true to set the response Content-Disposition to inline
*/
public RenderBinary(InputStream is, String name, String contentType, boolean inline) {
this.is = is;
this.name = name;
this.contentType = contentType;
this.inline = inline;
}
public RenderBinary(InputStream is, String name, long length, String contentType, boolean inline) {
this.is = is;
this.name = name;
this.contentType = contentType;
this.inline = inline;
this.length = length;
}
public RenderBinary(InputStream is, String name, long length, boolean inline) {
this.is = is;
this.name = name;
this.length = length;
this.inline = inline;
}
/**
* Send a file as the response. Content-disposion is set to attachment.
*
* @param file readable file to send back
* @param name a name to use as Content-disposion's filename
*/
public RenderBinary(File file, String name) {
this(file, name, false);
if (file == null) {
throw new RuntimeException("file is null");
}
}
/**
* Send a file as the response.
* Content-disposion is set to attachment, name is taken from file's name
* @param file readable file to send back
*/
public RenderBinary(File file) {
this(file, file.getName(), true);
}
/**
* Send a file as the response.
* Content-disposion is set to attachment, name is taken from file's name
* @param file readable file to send back
*/
public RenderBinary(File file, String name, boolean inline) {
this.file = file;
this.name = name;
this.inline = inline;
if (file == null) {
throw new RuntimeException("file is null");
}
}
@Override
public void apply(Request request, Response response) {
try {
if (name != null) {
setContentTypeIfNotSet(response, MimeTypes.getContentType(name));
}
if (contentType != null) {
response.contentType = contentType;
}
String dispositionType;
if(inline) {
dispositionType = INLINE_DISPOSITION_TYPE;
} else {
dispositionType = ATTACHMENT_DISPOSITION_TYPE;
}
if (!response.headers.containsKey("Content-Disposition")) {
if(name == null) {
response.setHeader("Content-Disposition", dispositionType);
} else {
if(canAsciiEncode(name)) {
String contentDisposition = "%s; filename=\"%s\"";
response.setHeader("Content-Disposition", String.format(contentDisposition, dispositionType, name));
} else {
final String encoding = getEncoding();
String contentDisposition = "%1$s; filename*="+encoding+"''%2$s; filename=\"%2$s\"";
response.setHeader("Content-Disposition", String.format(contentDisposition, dispositionType, encoder.encode(name, encoding)));
}
}
}
if (file != null) {
if (!file.exists()) {
throw new UnexpectedException("Your file does not exists (" + file + ")");
}
if (!file.canRead()) {
throw new UnexpectedException("Can't read your file (" + file + ")");
}
if (!file.isFile()) {
throw new UnexpectedException("Your file is not a real file (" + file + ")");
}
response.direct = file;
} else {
if (response.getHeader("Content-Length") != null) {
response.direct = is;
} else {
if (length != 0) {
response.setHeader("Content-Length", length + "");
response.direct = is;
} else {
byte[] buffer = new byte[8092];
int count = 0;
while ((count = is.read(buffer)) > 0) {
response.out.write(buffer, 0, count);
}
is.close();
}
}
}
} catch (Exception e) {
throw new UnexpectedException(e);
}
}
private boolean canAsciiEncode(String string) {
CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder();
return asciiEncoder.canEncode(string);
}
}