NIHVIVO-785 When the main image is uploaded, it is stored in the session, not on the Individual, until the thumbnail is cropped -- then both the main image and the thumbnail are stored on the Individual.
This commit is contained in:
parent
39ffb7c433
commit
4246a4385c
5 changed files with 489 additions and 192 deletions
|
@ -29,6 +29,7 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileServingHelper;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
|
||||||
import freemarker.template.Configuration;
|
import freemarker.template.Configuration;
|
||||||
|
|
||||||
|
@ -281,37 +282,22 @@ public class ImageUploadController extends FreeMarkerHttpServlet {
|
||||||
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
|
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
|
||||||
.getFullWebappDaoFactory());
|
.getFullWebappDaoFactory());
|
||||||
|
|
||||||
// Did they provide a file to upload? If not, show an error.
|
|
||||||
FileItem fileItem;
|
|
||||||
try {
|
try {
|
||||||
fileItem = helper.validateImageFromRequest(vreq);
|
// Did they provide a file to upload? If not, show an error.
|
||||||
|
FileItem fileItem = helper.validateImageFromRequest(vreq);
|
||||||
|
|
||||||
|
// Put it in the file system, and store a reference in the session.
|
||||||
|
FileInfo fileInfo = helper.storeNewImage(fileItem, vreq);
|
||||||
|
|
||||||
|
// How big is the new image? If not big enough, show an error.
|
||||||
|
Dimensions size = helper.getNewImageSize(fileInfo);
|
||||||
|
|
||||||
|
// Go to the cropping page.
|
||||||
|
return showCropImagePage(vreq, entity, fileInfo
|
||||||
|
.getBytestreamAliasUrl(), size);
|
||||||
} catch (UserMistakeException e) {
|
} catch (UserMistakeException e) {
|
||||||
return showErrorMessage(vreq, entity, e.getMessage());
|
return showErrorMessage(vreq, entity, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the old main image (if any) and store the new one.
|
|
||||||
helper.removeExistingImage(entity);
|
|
||||||
helper.storeMainImageFile(entity, fileItem);
|
|
||||||
|
|
||||||
// The entity Individual is stale - get another one;
|
|
||||||
String entityUri = entity.getURI();
|
|
||||||
entity = vreq.getFullWebappDaoFactory().getIndividualDao()
|
|
||||||
.getIndividualByURI(entityUri);
|
|
||||||
|
|
||||||
Dimensions mainImageSize = helper.getMainImageSize(entity);
|
|
||||||
|
|
||||||
if ((mainImageSize.height < THUMBNAIL_HEIGHT)
|
|
||||||
|| (mainImageSize.width < THUMBNAIL_WIDTH)) {
|
|
||||||
String message = "The uploaded image should be at least "
|
|
||||||
+ THUMBNAIL_HEIGHT + " pixels high and " + THUMBNAIL_WIDTH
|
|
||||||
+ " pixels wide.";
|
|
||||||
return showErrorMessage(vreq, entity, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the cropping page.
|
|
||||||
return showCropImagePage(vreq, entity, getMainImageUrl(entity),
|
|
||||||
mainImageSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -338,13 +324,18 @@ public class ImageUploadController extends FreeMarkerHttpServlet {
|
||||||
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
|
ImageUploadHelper helper = new ImageUploadHelper(fileStorage, vreq
|
||||||
.getFullWebappDaoFactory());
|
.getFullWebappDaoFactory());
|
||||||
|
|
||||||
validateMainImage(entity);
|
try {
|
||||||
CropRectangle crop = validateCropCoordinates(vreq);
|
CropRectangle crop = validateCropCoordinates(vreq);
|
||||||
|
FileInfo newImage = helper.getNewImageInfo(vreq);
|
||||||
|
FileInfo thumbnail = helper.generateThumbnail(crop, newImage);
|
||||||
|
|
||||||
helper.removeExistingThumbnail(entity);
|
helper.removeExistingImage(entity);
|
||||||
helper.generateThumbnailAndStore(entity, crop);
|
helper.storeImageFiles(entity, newImage, thumbnail);
|
||||||
|
|
||||||
return showExitPage(vreq, entity);
|
return showExitPage(vreq, entity);
|
||||||
|
} catch (UserMistakeException e) {
|
||||||
|
return showErrorMessage(vreq, entity, e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -379,17 +370,6 @@ public class ImageUploadController extends FreeMarkerHttpServlet {
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* We can't do a thumbnail if there is no main image.
|
|
||||||
*/
|
|
||||||
private void validateMainImage(Individual entity) {
|
|
||||||
if (entity.getMainImageUri() == null) {
|
|
||||||
throw new IllegalStateException("Can't store a thumbnail "
|
|
||||||
+ "on an individual with no main image: '"
|
|
||||||
+ showEntity(entity) + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Did we get the cropping coordinates?
|
* Did we get the cropping coordinates?
|
||||||
*/
|
*/
|
||||||
|
@ -552,19 +532,6 @@ public class ImageUploadController extends FreeMarkerHttpServlet {
|
||||||
return UrlBuilder.getPath(URL_HERE, params);
|
return UrlBuilder.getPath(URL_HERE, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format an entity for display in a message.
|
|
||||||
*/
|
|
||||||
private String showEntity(Individual entity) {
|
|
||||||
if (entity == null) {
|
|
||||||
return String.valueOf(null);
|
|
||||||
} else if (entity.getName() == null) {
|
|
||||||
return "'no name' (" + entity.getURI() + ")";
|
|
||||||
} else {
|
|
||||||
return "'" + entity.getName() + "' (" + entity.getURI() + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the entity's name for display as part of the page title.
|
* Format the entity's name for display as part of the page title.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,13 +31,16 @@ import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.CropRectangle;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.Dimensions;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.Dimensions;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.UserMistakeException;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.ImageUploadController.UserMistakeException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.FileModelHelper;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.TempFileHolder;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
|
import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServletRequest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +49,12 @@ import edu.cornell.mannlib.vitro.webapp.filestorage.uploadrequest.FileUploadServ
|
||||||
public class ImageUploadHelper {
|
public class ImageUploadHelper {
|
||||||
private static final Log log = LogFactory.getLog(ImageUploadHelper.class);
|
private static final Log log = LogFactory.getLog(ImageUploadHelper.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When they upload a new image, store it as this session attribute until
|
||||||
|
* we're ready to attach it to the Individual.
|
||||||
|
*/
|
||||||
|
public static final String ATTRIBUTE_TEMP_FILE = "ImageUploadHelper.tempFile";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the main image is larger than this, it will be displayed at reduced
|
* If the main image is larger than this, it will be displayed at reduced
|
||||||
* scale.
|
* scale.
|
||||||
|
@ -65,10 +74,12 @@ public class ImageUploadHelper {
|
||||||
return Collections.unmodifiableMap(map);
|
return Collections.unmodifiableMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final WebappDaoFactory webAppDaoFactory;
|
||||||
private final FileModelHelper fileModelHelper;
|
private final FileModelHelper fileModelHelper;
|
||||||
private final FileStorage fileStorage;
|
private final FileStorage fileStorage;
|
||||||
|
|
||||||
ImageUploadHelper(FileStorage fileStorage, WebappDaoFactory webAppDaoFactory) {
|
ImageUploadHelper(FileStorage fileStorage, WebappDaoFactory webAppDaoFactory) {
|
||||||
|
this.webAppDaoFactory = webAppDaoFactory;
|
||||||
this.fileModelHelper = new FileModelHelper(webAppDaoFactory);
|
this.fileModelHelper = new FileModelHelper(webAppDaoFactory);
|
||||||
this.fileStorage = fileStorage;
|
this.fileStorage = fileStorage;
|
||||||
}
|
}
|
||||||
|
@ -118,12 +129,158 @@ public class ImageUploadHelper {
|
||||||
String mimeType = getMimeType(file);
|
String mimeType = getMimeType(file);
|
||||||
if (!RECOGNIZED_FILE_TYPES.containsValue(mimeType)) {
|
if (!RECOGNIZED_FILE_TYPES.containsValue(mimeType)) {
|
||||||
throw new UserMistakeException("'" + filename
|
throw new UserMistakeException("'" + filename
|
||||||
+ "' is not a recognized image file type. Please upload JPEG, GIF, or PNG files only.");
|
+ "' is not a recognized image file type. "
|
||||||
|
+ "Please upload JPEG, GIF, or PNG files only.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The user has uploaded a new main image, but we're not ready to assign it
|
||||||
|
* to them.
|
||||||
|
*
|
||||||
|
* Put it into the file storage system, and attach it as a temp file on the
|
||||||
|
* session until we need it.
|
||||||
|
*/
|
||||||
|
FileInfo storeNewImage(FileItem fileItem, VitroRequest vreq) {
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
inputStream = fileItem.getInputStream();
|
||||||
|
String mimeType = getMimeType(fileItem);
|
||||||
|
String filename = getSimpleFilename(fileItem);
|
||||||
|
WebappDaoFactory wadf = vreq.getWebappDaoFactory();
|
||||||
|
|
||||||
|
FileInfo fileInfo = FileModelHelper.createFile(fileStorage, wadf,
|
||||||
|
filename, mimeType, inputStream);
|
||||||
|
|
||||||
|
TempFileHolder.attach(vreq.getSession(), ATTRIBUTE_TEMP_FILE,
|
||||||
|
fileInfo);
|
||||||
|
|
||||||
|
return fileInfo;
|
||||||
|
} catch (FileAlreadyExistsException e) {
|
||||||
|
throw new IllegalStateException("Can't create the new image file.",
|
||||||
|
e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Can't create the new image file.",
|
||||||
|
e);
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
inputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find out how big this image is.
|
||||||
|
*
|
||||||
|
* @throws UserMistakeException
|
||||||
|
* if the image is smaller than a thumbnail.
|
||||||
|
*/
|
||||||
|
Dimensions getNewImageSize(FileInfo fileInfo) throws UserMistakeException {
|
||||||
|
String uri = fileInfo.getBytestreamUri();
|
||||||
|
String filename = fileInfo.getFilename();
|
||||||
|
|
||||||
|
InputStream stream = null;
|
||||||
|
try {
|
||||||
|
stream = fileStorage.getInputStream(uri, filename);
|
||||||
|
BufferedImage i = ImageIO.read(stream);
|
||||||
|
Dimensions size = new Dimensions(i.getWidth(), i.getHeight());
|
||||||
|
log.debug("new image size is " + size);
|
||||||
|
|
||||||
|
if ((size.height < THUMBNAIL_HEIGHT)
|
||||||
|
|| (size.width < THUMBNAIL_WIDTH)) {
|
||||||
|
throw new UserMistakeException(
|
||||||
|
"The uploaded image should be at least "
|
||||||
|
+ THUMBNAIL_HEIGHT + " pixels high and "
|
||||||
|
+ THUMBNAIL_WIDTH + " pixels wide.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new IllegalStateException("File not found: " + fileInfo, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Can't read image file: "
|
||||||
|
+ fileInfo, e);
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
try {
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the info for the new image, from where we stored it in the session.
|
||||||
|
*
|
||||||
|
* @throws UserMistakeException
|
||||||
|
* if it isn't there.
|
||||||
|
*/
|
||||||
|
FileInfo getNewImageInfo(VitroRequest vreq) throws UserMistakeException {
|
||||||
|
FileInfo fileInfo = TempFileHolder.remove(vreq.getSession(),
|
||||||
|
ATTRIBUTE_TEMP_FILE);
|
||||||
|
|
||||||
|
if (fileInfo == null) {
|
||||||
|
throw new UserMistakeException(
|
||||||
|
"There is no image file to be cropped.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crop the main image to create the thumbnail, and put it into the file
|
||||||
|
* storage system.
|
||||||
|
*/
|
||||||
|
FileInfo generateThumbnail(CropRectangle crop, FileInfo newImage) {
|
||||||
|
InputStream mainStream = null;
|
||||||
|
InputStream thumbStream = null;
|
||||||
|
try {
|
||||||
|
String mainBytestreamUri = newImage.getBytestreamUri();
|
||||||
|
String mainFilename = newImage.getFilename();
|
||||||
|
mainStream = fileStorage.getInputStream(mainBytestreamUri,
|
||||||
|
mainFilename);
|
||||||
|
|
||||||
|
thumbStream = scaleImageForThumbnail(mainStream, crop);
|
||||||
|
|
||||||
|
String mimeType = RECOGNIZED_FILE_TYPES.get(".jpg");
|
||||||
|
String filename = createThumbnailFilename(mainFilename);
|
||||||
|
|
||||||
|
FileInfo fileInfo = FileModelHelper.createFile(fileStorage,
|
||||||
|
webAppDaoFactory, filename, mimeType, thumbStream);
|
||||||
|
log.debug("Created thumbnail: " + fileInfo);
|
||||||
|
return fileInfo;
|
||||||
|
} catch (FileAlreadyExistsException e) {
|
||||||
|
throw new IllegalStateException("Can't create the thumbnail file: "
|
||||||
|
+ e.getMessage(), e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Can't create the thumbnail file",
|
||||||
|
e);
|
||||||
|
} finally {
|
||||||
|
if (mainStream != null) {
|
||||||
|
try {
|
||||||
|
mainStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (thumbStream != null) {
|
||||||
|
try {
|
||||||
|
thumbStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this entity already had a main image, remove the connection. If the
|
* If this entity already had a main image, remove the connection. If the
|
||||||
* image and the thumbnail are no longer used by anyone, remove them from
|
* image and the thumbnail are no longer used by anyone, remove them from
|
||||||
|
@ -154,47 +311,6 @@ public class ImageUploadHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Store this image in the model and in the file storage system, and set it
|
|
||||||
* as the main image for this person.
|
|
||||||
*/
|
|
||||||
void storeMainImageFile(Individual person, FileItem imageFileItem) {
|
|
||||||
InputStream inputStream = null;
|
|
||||||
try {
|
|
||||||
inputStream = imageFileItem.getInputStream();
|
|
||||||
String mimeType = getMimeType(imageFileItem);
|
|
||||||
String filename = getSimpleFilename(imageFileItem);
|
|
||||||
|
|
||||||
// Create the file individuals in the model
|
|
||||||
Individual byteStream = fileModelHelper
|
|
||||||
.createByteStreamIndividual();
|
|
||||||
Individual file = fileModelHelper.createFileIndividual(mimeType,
|
|
||||||
filename, byteStream);
|
|
||||||
|
|
||||||
// Store the file in the FileStorage system.
|
|
||||||
fileStorage.createFile(byteStream.getURI(), filename, inputStream);
|
|
||||||
|
|
||||||
// Set the file as the main image for the person.
|
|
||||||
fileModelHelper.setAsMainImageOnEntity(person, file);
|
|
||||||
} catch (FileAlreadyExistsException e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Can't create the main image file for '" + person.getName()
|
|
||||||
+ "' (" + person.getURI() + ")" + e.getMessage(), e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Can't create the main image file for '" + person.getName()
|
|
||||||
+ "' (" + person.getURI() + ")", e);
|
|
||||||
} finally {
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the entity already has a thumbnail, remove it. If there are no other
|
* If the entity already has a thumbnail, remove it. If there are no other
|
||||||
* references to the thumbnail, delete it from the model and from the file
|
* references to the thumbnail, delete it from the model and from the file
|
||||||
|
@ -227,64 +343,12 @@ public class ImageUploadHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a thumbnail from the main image from it, store it in the model
|
* Store the image on the entity, and the thumbnail on the image.
|
||||||
* and in the file storage system, and set it as the thumbnail on the main
|
|
||||||
* image.
|
|
||||||
*/
|
*/
|
||||||
void generateThumbnailAndStore(Individual person,
|
void storeImageFiles(Individual entity, FileInfo newImage,
|
||||||
ImageUploadController.CropRectangle crop) {
|
FileInfo thumbnail) {
|
||||||
String mainBytestreamUri = FileModelHelper
|
FileModelHelper.setImagesOnEntity(webAppDaoFactory, entity, newImage,
|
||||||
.getMainImageBytestreamUri(person);
|
thumbnail);
|
||||||
String mainFilename = FileModelHelper.getMainImageFilename(person);
|
|
||||||
if (mainBytestreamUri == null) {
|
|
||||||
log.warn("Tried to generate a thumbnail on '" + person.getURI()
|
|
||||||
+ "', but there was no main image.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream mainInputStream = null;
|
|
||||||
InputStream thumbInputStream = null;
|
|
||||||
try {
|
|
||||||
mainInputStream = fileStorage.getInputStream(mainBytestreamUri,
|
|
||||||
mainFilename);
|
|
||||||
thumbInputStream = scaleImageForThumbnail(mainInputStream, crop);
|
|
||||||
String mimeType = RECOGNIZED_FILE_TYPES.get(".jpg");
|
|
||||||
String filename = createThumbnailFilename(mainFilename);
|
|
||||||
|
|
||||||
// Create the file individuals in the model
|
|
||||||
Individual byteStream = fileModelHelper
|
|
||||||
.createByteStreamIndividual();
|
|
||||||
Individual file = fileModelHelper.createFileIndividual(mimeType,
|
|
||||||
filename, byteStream);
|
|
||||||
|
|
||||||
// Store the file in the FileStorage system.
|
|
||||||
fileStorage.createFile(byteStream.getURI(), filename,
|
|
||||||
thumbInputStream);
|
|
||||||
|
|
||||||
// Set the file as the thumbnail on the main image for the person.
|
|
||||||
fileModelHelper.setThumbnailOnIndividual(person, file);
|
|
||||||
} catch (FileAlreadyExistsException e) {
|
|
||||||
throw new IllegalStateException("Can't create the thumbnail file: "
|
|
||||||
+ e.getMessage(), e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new IllegalStateException("Can't create the thumbnail file",
|
|
||||||
e);
|
|
||||||
} finally {
|
|
||||||
if (mainInputStream != null) {
|
|
||||||
try {
|
|
||||||
mainInputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (thumbInputStream != null) {
|
|
||||||
try {
|
|
||||||
thumbInputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -398,42 +462,4 @@ public class ImageUploadHelper {
|
||||||
return crop.unscale(displayScale);
|
return crop.unscale(displayScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find out how big the main image is.
|
|
||||||
*/
|
|
||||||
Dimensions getMainImageSize(Individual entity) {
|
|
||||||
String uri = FileModelHelper.getMainImageBytestreamUri(entity);
|
|
||||||
String filename = FileModelHelper.getMainImageFilename(entity);
|
|
||||||
InputStream stream = null;
|
|
||||||
try {
|
|
||||||
stream = fileStorage.getInputStream(uri, filename);
|
|
||||||
BufferedImage i = ImageIO.read(stream);
|
|
||||||
Dimensions size = new Dimensions(i.getWidth(), i.getHeight());
|
|
||||||
log.debug("main image size is " + size);
|
|
||||||
return size;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
log.error(
|
|
||||||
"No main image file for '" + showUri(entity) + "'; name='"
|
|
||||||
+ filename + "', bytestreamUri='" + uri + "'", e);
|
|
||||||
return new Dimensions(0, 0);
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error(
|
|
||||||
"Can't read main image file for '" + showUri(entity)
|
|
||||||
+ "'; name='" + filename + "', bytestreamUri='"
|
|
||||||
+ uri + "'", e);
|
|
||||||
return new Dimensions(0, 0);
|
|
||||||
} finally {
|
|
||||||
if (stream != null) {
|
|
||||||
try {
|
|
||||||
stream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String showUri(Individual entity) {
|
|
||||||
return (entity == null) ? "null" : entity.getURI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
@ -19,6 +21,9 @@ import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileAlreadyExistsException;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -30,10 +35,66 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||||
* a parameter holds all necessary references for the operation. Other methods
|
* a parameter holds all necessary references for the operation. Other methods
|
||||||
* require an instance, which is initialized with a {@link WebappDaoFactory}.
|
* require an instance, which is initialized with a {@link WebappDaoFactory}.
|
||||||
* </p>
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* TODO: This should be based around FileInfo and ImageInfo, as much as
|
||||||
|
* possible.
|
||||||
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class FileModelHelper {
|
public class FileModelHelper {
|
||||||
private static final Log log = LogFactory.getLog(FileModelHelper.class);
|
private static final Log log = LogFactory.getLog(FileModelHelper.class);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Methods based around FileInfo
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static FileInfo createFile(FileStorage fileStorage,
|
||||||
|
WebappDaoFactory wadf, String filename, String mimeType,
|
||||||
|
InputStream inputStream) throws FileAlreadyExistsException,
|
||||||
|
IOException {
|
||||||
|
FileModelHelper fmh = new FileModelHelper(wadf);
|
||||||
|
|
||||||
|
// Create the file individuals in the model
|
||||||
|
Individual byteStream = fmh.createByteStreamIndividual();
|
||||||
|
String bytestreamUri = byteStream.getURI();
|
||||||
|
Individual file = fmh.createFileIndividual(mimeType, filename,
|
||||||
|
byteStream);
|
||||||
|
String fileUri = file.getURI();
|
||||||
|
|
||||||
|
// Store the file in the FileStorage system.
|
||||||
|
fileStorage.createFile(bytestreamUri, filename, inputStream);
|
||||||
|
|
||||||
|
// Figure out the alias URL
|
||||||
|
String aliasUrl = FileServingHelper.getBytestreamAliasUrl(
|
||||||
|
bytestreamUri, filename);
|
||||||
|
|
||||||
|
// And wrap it all up in a tidy little package.
|
||||||
|
return new FileInfo.Builder().setFilename(filename).setMimeType(
|
||||||
|
mimeType).setUri(fileUri).setBytestreamUri(bytestreamUri)
|
||||||
|
.setBytestreamAliasUrl(aliasUrl).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record this image file and thumbnail on this entity. NOTE: after this
|
||||||
|
* update, the entity object is stale.
|
||||||
|
*/
|
||||||
|
public static void setImagesOnEntity(WebappDaoFactory wadf,
|
||||||
|
Individual entity, FileInfo mainInfo, FileInfo thumbInfo) {
|
||||||
|
IndividualDao individualDao = wadf.getIndividualDao();
|
||||||
|
|
||||||
|
// Add the thumbnail file to the main image file.
|
||||||
|
ObjectPropertyStatementDao opsd = wadf.getObjectPropertyStatementDao();
|
||||||
|
opsd.insertNewObjectPropertyStatement(new ObjectPropertyStatementImpl(
|
||||||
|
mainInfo.getUri(), VitroVocabulary.FS_THUMBNAIL_IMAGE,
|
||||||
|
thumbInfo.getUri()));
|
||||||
|
|
||||||
|
// Add the main image file to the entity.
|
||||||
|
entity.setMainImageUri(mainInfo.getUri());
|
||||||
|
individualDao.updateIndividual(entity);
|
||||||
|
|
||||||
|
log.debug("Set images on '" + entity.getURI() + "': main=" + mainInfo
|
||||||
|
+ ", thumb=" + thumbInfo);
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
// Static methods -- the Individual holds all necessary references.
|
// Static methods -- the Individual holds all necessary references.
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import javax.servlet.http.HttpSessionBindingEvent;
|
||||||
|
import javax.servlet.http.HttpSessionBindingListener;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.backend.FileStorageSetup;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.filestorage.model.FileInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an uploaded file to the session with a listener, so the file will be
|
||||||
|
* deleted if
|
||||||
|
* <ul>
|
||||||
|
* <li>The session times out.</li>
|
||||||
|
* <li>The session is invalidated.</li>
|
||||||
|
* <li>The server is shut down.</li>
|
||||||
|
* <li>Another file is attached to the session with the same attribute name.</li>
|
||||||
|
* </ul>
|
||||||
|
* To see that the file isn't deleted, remove it from the session with a call to
|
||||||
|
* {@link TempFileHolder#remove(HttpSession, String) remove}.
|
||||||
|
*/
|
||||||
|
public class TempFileHolder implements HttpSessionBindingListener {
|
||||||
|
private static final Log log = LogFactory.getLog(TempFileHolder.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link TempFileHolder} holding the given {@link FileInfo}, and
|
||||||
|
* attach it to the session with the given attribute name.
|
||||||
|
*
|
||||||
|
* If an attribute with this name already exists, it is replaced.
|
||||||
|
*/
|
||||||
|
public static void attach(HttpSession session, String attributeName,
|
||||||
|
FileInfo fileInfo) {
|
||||||
|
if (session == null) {
|
||||||
|
throw new NullPointerException("session may not be null.");
|
||||||
|
}
|
||||||
|
if (attributeName == null) {
|
||||||
|
throw new NullPointerException("attributeName may not be null.");
|
||||||
|
}
|
||||||
|
if (fileInfo == null) {
|
||||||
|
throw new NullPointerException("fileInfo may not be null.");
|
||||||
|
}
|
||||||
|
log.debug("attach this file: " + fileInfo);
|
||||||
|
session.setAttribute(attributeName, new TempFileHolder(fileInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link TempFileHolder} which is stored as an attribute on this
|
||||||
|
* session, extract the {@link FileInfo} from it, and remove it from the
|
||||||
|
* session.
|
||||||
|
*
|
||||||
|
* If there is no such attribute, of if it is not a {@link TempFileHolder},
|
||||||
|
* return null.
|
||||||
|
*/
|
||||||
|
public static FileInfo remove(HttpSession session, String attributeName) {
|
||||||
|
if (session == null) {
|
||||||
|
throw new NullPointerException("session may not be null.");
|
||||||
|
}
|
||||||
|
if (attributeName == null) {
|
||||||
|
throw new NullPointerException("attributeName may not be null.");
|
||||||
|
}
|
||||||
|
Object attribute = session.getAttribute(attributeName);
|
||||||
|
if (attribute instanceof TempFileHolder) {
|
||||||
|
FileInfo fileInfo = ((TempFileHolder) attribute).extractFileInfo();
|
||||||
|
session.removeAttribute(attributeName);
|
||||||
|
log.debug("remove this file: " + fileInfo);
|
||||||
|
return fileInfo;
|
||||||
|
} else if (attribute == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
session.removeAttribute(attributeName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FileInfo fileInfo;
|
||||||
|
|
||||||
|
private TempFileHolder(FileInfo fileInfo) {
|
||||||
|
if (fileInfo == null) {
|
||||||
|
throw new NullPointerException("fileInfo may not be null.");
|
||||||
|
}
|
||||||
|
this.fileInfo = fileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link FileInfo} payload, and removes it so the file won't be
|
||||||
|
* deleted when the value is unbound.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private FileInfo extractFileInfo() {
|
||||||
|
FileInfo result = this.fileInfo;
|
||||||
|
this.fileInfo = null;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When attached to the session, do nothing.
|
||||||
|
*
|
||||||
|
* @see HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void valueBound(HttpSessionBindingEvent event) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When removed from the session, if the {@link #fileInfo} is not empty,
|
||||||
|
* delete the file. If you had wanted this file, you should have called
|
||||||
|
* {@link #remove(HttpSession, String) remove}.
|
||||||
|
*
|
||||||
|
* @see HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void valueUnbound(HttpSessionBindingEvent event) {
|
||||||
|
if (fileInfo == null) {
|
||||||
|
log.trace("No file info.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileInfo.getBytestreamUri() == null) {
|
||||||
|
log.warn("File info has no URI?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpSession session = event.getSession();
|
||||||
|
ServletContext servletContext = session.getServletContext();
|
||||||
|
|
||||||
|
FileStorage fs = (FileStorage) servletContext
|
||||||
|
.getAttribute(FileStorageSetup.ATTRIBUTE_NAME);
|
||||||
|
if (fs == null) {
|
||||||
|
log.error("Servlet context does not contain file storage at '"
|
||||||
|
+ FileStorageSetup.ATTRIBUTE_NAME + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.deleteFile(fileInfo.getBytestreamUri());
|
||||||
|
log.debug("Deleted file " + fileInfo);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Failed to delete temp file from session: " + event, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vitro.webapp.filestorage.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable packet of information about an uploaded file, with a builder
|
||||||
|
* class to permit incremental construction.
|
||||||
|
*/
|
||||||
|
public class FileInfo {
|
||||||
|
private final String uri;
|
||||||
|
private final String filename;
|
||||||
|
private final String mimeType;
|
||||||
|
private final String bytestreamUri;
|
||||||
|
private final String bytestreamAliasUrl;
|
||||||
|
|
||||||
|
private FileInfo(Builder builder) {
|
||||||
|
this.uri = builder.uri;
|
||||||
|
this.filename = builder.filename;
|
||||||
|
this.mimeType = builder.mimeType;
|
||||||
|
this.bytestreamUri = builder.bytestreamUri;
|
||||||
|
this.bytestreamAliasUrl = builder.bytestreamAliasUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUri() {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMimeType() {
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBytestreamUri() {
|
||||||
|
return bytestreamUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBytestreamAliasUrl() {
|
||||||
|
return bytestreamAliasUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FileInfo[uri=" + uri + ", filename=" + filename + ", mimeType="
|
||||||
|
+ mimeType + ", bytestreamUri=" + bytestreamUri + ", aliasUrl="
|
||||||
|
+ bytestreamAliasUrl + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder class allows us to supply the values one at a time, and then
|
||||||
|
* freeze them into an immutable object.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private String uri;
|
||||||
|
private String filename;
|
||||||
|
private String mimeType;
|
||||||
|
private String bytestreamUri;
|
||||||
|
private String bytestreamAliasUrl;
|
||||||
|
|
||||||
|
public Builder setUri(String uri) {
|
||||||
|
this.uri = uri;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setFilename(String filename) {
|
||||||
|
this.filename = filename;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setMimeType(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setBytestreamUri(String bytestreamUri) {
|
||||||
|
this.bytestreamUri = bytestreamUri;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setBytestreamAliasUrl(String bytestreamAliasUrl) {
|
||||||
|
this.bytestreamAliasUrl = bytestreamAliasUrl;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileInfo build() {
|
||||||
|
return new FileInfo(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue