-
Notifications
You must be signed in to change notification settings - Fork 9
Upload
Marc Larue edited this page Apr 19, 2016
·
5 revisions
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import se.rupy.http.Deploy;
import se.rupy.http.Event;
import se.rupy.http.Input;
import se.rupy.http.Output;
import se.rupy.http.Query;
import se.rupy.http.Root;
import se.rupy.http.Service;
import se.rupy.http.User;
/*
* This service only supports one file at the time.
*/
public class Upload extends Service {
private static int SIZE = 1024;
public String path() { return "/upload"; }
public void filter(Event event) throws Event, Exception {
Output out = event.output();
if(event.query().method() == Query.POST) {
Item item = new Item();
item.path = "file";
item = save(event, item);
out.println("File deployed!");
}
else {
out.print("<form enctype=\"multipart/form-data\" name=\"upload\" action=\"upload\" method=\"post\">");
out.print("<input name=\"file\" type=\"file\"/>");
out.print("<input type=\"submit\">");
out.print("</form>");
}
}
public static Item save(Event event, Item item) throws Event, Exception {
String type = event.query().header("content-type");
String boundary = "--" + unquote(type.substring(type.indexOf("boundary=") + 9));
Input in = event.input();
String line = in.line();
while(line != null) {
/*
* find boundary
*/
if(line.equals(boundary + "--")) {
User.redirect(event, "/");
}
if(line.equals(boundary)) {
line = in.line();
/*
* read headers; parse filename and content-type
*/
while(line != null && !line.equals("")) {
int colon = line.indexOf(":");
if (colon > -1) {
String name = line.substring(0, colon).toLowerCase();
String value = line.substring(colon + 1).trim();
if(name.equals("content-disposition")) {
item.name = unpath(unquote(value.substring(value.indexOf("filename=") + 9).trim()));
}
if(name.equals("content-type")) {
item.type = value;
}
}
line = in.line();
}
if(item.name == null || item.name.length() == 0) {
User.redirect(event, "/");
}
/*
* create path and file
*/
String home = "app/" + Root.host();
java.io.File path = new java.io.File(home + "/" + item.path);
if(!path.exists()) {
path.mkdirs();
}
item.file = new java.io.File(home + "/" + item.path + "/" + item.name);
FileOutputStream out = new FileOutputStream(item.file);
/*
* stream data
*/
Boundary bound = new Boundary();
bound.value = boundary.getBytes();
byte[] data = new byte[SIZE];
int read = in.read(data);
while(read > -1) {
try {
out.write(data, 0, bound.find(read, data, out));
}
catch(Boundary.EOB eob) {
out.write(data, 0, eob.index - 2); // remove the last trailing \r\n
out.flush();
out.close();
// only handles one file for now,
// need to rewind the stream for
// multiple files.
return item;
}
read = in.read(data);
}
throw new IOException("Boundary not found. (trailing)");
}
line = in.line();
}
throw new IOException("Boundary not found. (initing)");
}
static String unquote(String value) {
int index = value.lastIndexOf("\"");
if(index > -1) {
return value.substring(1, index);
}
return value;
}
static String unpath(String value) {
int index = Math.max(value.lastIndexOf('/'), value.lastIndexOf('\\'));
if(index > -1) {
return value.substring(index + 1);
}
return value;
}
public static class Item {
String name;
String path;
String type;
java.io.File file;
public String toString() {
return name + " " + path + " " + type;
}
}
/*
* The multipart protocol was poorly designed, so we have to check
* for the boundary for every byte and handle things if the boundary
* is between buffers.
*
* TODO: An alternative way is to write everything from headers onwards
* to a file and then search for the boundary starting at the end of the
* file, and crop the boundary.
*/
public static class Boundary {
byte[] value;
int index; // remember boundary index between finds
int find(int read, byte[] data, OutputStream out) throws EOB, IOException {
int wrap = index;
for(int i = 0; i < read; i++) {
if(data[i] == value[index]) { // maybe boundary
if(index == value.length - 1) { // boundary
int start = i - value.length + 1;
throw new EOB(start > -1 ? start : 0);
}
index++;
}
else { // not boundary
if(wrap > 0) { // write non-boundary from last find
out.write(value, 0, wrap);
wrap = 0;
}
index = 0;
}
}
return read - index;
}
public static class EOB extends Throwable {
int index;
public EOB(int index) {
this.index = index;
}
}
}
}