package org.karmaexchange.util;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.List;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.io.IOUtils;
import org.karmaexchange.resources.msg.ErrorResponseMsg;
import org.karmaexchange.resources.msg.ErrorResponseMsg.ErrorInfo;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;
import com.google.appengine.api.images.Image;
import com.google.appengine.api.images.ImagesService;
import com.google.appengine.api.images.ImagesServiceFactory;
import com.google.appengine.api.images.Transform;
import com.google.common.collect.Lists;
public class ImageUploadUtil {
// Copy facebook which seems to use 2048.
private final static int DEFAULT_MAX_IMAGE_PX = 2048;
public static BlobKey persistImage(HttpServletRequest req) {
return persistImages(req, 1).get(0);
}
public static List<BlobKey> persistImages(HttpServletRequest req, int limit) {
return persistImages(req, limit, DEFAULT_MAX_IMAGE_PX, DEFAULT_MAX_IMAGE_PX, null);
}
// TODO(avaliani): enforce limit.
public static List<BlobKey> persistImages(HttpServletRequest req, int limit, int maxWidthPx,
int maxHeightPx, @Nullable MultivaluedMap<String, String> formFields) {
List<BlobKey> blobKeys = Lists.newArrayList();
ServletFileUpload servletFileUpload = new ServletFileUpload();
try {
FileItemIterator fileIter = servletFileUpload.getItemIterator(req);
while (fileIter.hasNext()) {
FileItemStream item = fileIter.next();
InputStream stream = item.openStream();
if (item.isFormField()) {
if (formFields != null) {
formFields.add(item.getFieldName(), Streams.asString(stream));
}
} else {
Image image = processImage(stream, maxWidthPx, maxHeightPx);
blobKeys.add(writeToFile(image));
}
}
} catch(IOException e) {
deleteBlobs(blobKeys);
throw ErrorResponseMsg.createException(e, ErrorInfo.Type.BAD_REQUEST);
} catch(FileUploadException e) {
deleteBlobs(blobKeys);
throw ErrorResponseMsg.createException(e, ErrorInfo.Type.BAD_REQUEST);
}
if (blobKeys.size() == 0) {
throw ErrorResponseMsg.createException(
"request does not contain any images", ErrorInfo.Type.BAD_REQUEST);
}
return blobKeys;
}
private static void deleteBlobs(List<BlobKey> blobKeys) {
BlobstoreServiceFactory.getBlobstoreService().delete(blobKeys.toArray(new BlobKey[0]));
}
private static Image processImage(InputStream inputStream, int maxWidthPx, int maxHeightPx)
throws IOException {
ImagesService imagesService = ImagesServiceFactory.getImagesService();
Image inputImage = ImagesServiceFactory.makeImage(IOUtils.toByteArray(inputStream));
Transform resize = ImagesServiceFactory.makeResize(maxWidthPx, maxHeightPx);
return imagesService.applyTransform(resize, inputImage);
}
private static BlobKey writeToFile(Image image) throws IOException {
FileService fileService = FileServiceFactory.getFileService();
AppEngineFile file = fileService.createNewBlobFile(toMimeType(image.getFormat()));
FileWriteChannel writeChannel = fileService.openWriteChannel(file, true);
writeChannel.write(ByteBuffer.wrap(image.getImageData()));
writeChannel.closeFinally();
return fileService.getBlobKey(file);
}
private static String toMimeType(Image.Format format) {
switch (format) {
case ICO:
return "image/x-icon";
default:
return "image/" + format.name().toLowerCase();
}
}
}