2 * NuxeoImageUtils.java
\r
4 * {Purpose of This Class}
\r
6 * {Other Notes Relating to This Class (Optional)}
\r
9 * $LastChangedRevision: $
\r
10 * $LastChangedDate: $
\r
12 * This document is a part of the source code and related artifacts
\r
13 * for CollectionSpace, an open source collections management system
\r
14 * for museums and related institutions:
\r
16 * http://www.collectionspace.org
\r
17 * http://wiki.collectionspace.org
\r
19 * Copyright © 2009 {Contributing Institution}.
\r
21 * Licensed under the Educational Community License (ECL), Version 2.0.
\r
22 * You may not use this file except in compliance with this License.
\r
24 * You may obtain a copy of the ECL 2.0 License at
\r
25 * https://source.collectionspace.org/collection-space/LICENSE.txt
\r
27 package org.collectionspace.services.common.imaging.nuxeo;
\r
29 import java.awt.Color;
\r
30 import java.awt.Font;
\r
31 import java.awt.Graphics;
\r
32 import java.awt.image.BufferedImage;
\r
33 import java.io.File;
\r
34 import java.io.ByteArrayOutputStream;
\r
35 import java.io.InputStream;
\r
36 import java.io.BufferedInputStream;
\r
37 import java.io.IOException;
\r
38 import java.math.BigDecimal;
\r
39 import java.math.BigInteger;
\r
40 import java.util.HashMap;
\r
41 import java.util.List;
\r
42 import java.util.Map;
\r
43 import java.util.Random;
\r
45 import javax.imageio.ImageIO;
\r
47 import org.nuxeo.runtime.api.Framework;
\r
48 //import org.nuxeo.runtime.api.ServiceManager;
\r
49 //import org.nuxeo.runtime.api.ServiceDescriptor;
\r
51 //import org.nuxeo.common.utils.FileUtils;
\r
53 import org.nuxeo.ecm.platform.picture.api.ImageInfo;
\r
54 import org.nuxeo.ecm.platform.picture.api.ImagingService;
\r
55 import org.nuxeo.ecm.platform.picture.api.PictureView;
\r
57 import org.nuxeo.ecm.platform.mimetype.MimetypeDetectionException;
\r
58 import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
\r
59 import org.nuxeo.ecm.platform.picture.api.adapters.PictureBlobHolder;
\r
60 import org.nuxeo.ecm.platform.filemanager.api.FileManager;
\r
61 import org.nuxeo.ecm.platform.types.TypeManager;
\r
63 import org.nuxeo.ecm.core.repository.RepositoryDescriptor;
\r
64 import org.nuxeo.ecm.core.repository.RepositoryManager;
\r
66 import org.nuxeo.ecm.core.repository.RepositoryService;
\r
67 //import org.nuxeo.runtime.model.ComponentManager;
\r
68 //import org.nuxeo.runtime.model.impl.ComponentManagerImpl;
\r
69 //import org.nuxeo.ecm.core.api.ejb.DocumentManagerBean;
\r
70 //import org.nuxeo.ecm.core.storage.sql.RepositoryImpl;
\r
71 //import org.nuxeo.ecm.core.storage.sql.Repository;
\r
72 import org.nuxeo.ecm.core.storage.sql.DefaultBinaryManager;
\r
73 import org.nuxeo.ecm.core.storage.sql.coremodel.SQLRepository;
\r
74 //import org.nuxeo.ecm.core.storage.sql.RepositoryDescriptor;
\r
76 //import org.nuxeo.ecm.core.api.DocumentResolver;
\r
77 import org.nuxeo.ecm.core.api.IdRef;
\r
78 import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
\r
79 import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;
\r
80 import org.nuxeo.ecm.core.api.impl.blob.FileBlob;
\r
81 import org.nuxeo.ecm.core.api.impl.blob.StreamingBlob;
\r
82 import org.nuxeo.ecm.core.api.impl.blob.ByteArrayBlob;
\r
83 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
\r
84 import org.nuxeo.ecm.core.api.repository.Repository;
\r
85 import org.nuxeo.ecm.core.api.Blob;
\r
86 import org.nuxeo.ecm.core.api.ClientException;
\r
87 import org.nuxeo.ecm.core.api.DocumentModel;
\r
88 import org.nuxeo.ecm.core.api.DocumentRef;
\r
90 import org.nuxeo.ecm.core.schema.DocumentType;
\r
91 import org.nuxeo.ecm.core.schema.SchemaManager;
\r
92 import org.nuxeo.ecm.core.schema.types.Schema;
\r
94 import org.slf4j.Logger;
\r
95 import org.slf4j.LoggerFactory;
\r
96 //import org.nuxeo.ecm.core.repository.jcr.testing.RepositoryOSGITestCase;
\r
98 import org.collectionspace.services.common.blob.BlobInput;
\r
99 import org.collectionspace.services.common.context.ServiceContext;
\r
100 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
\r
101 import org.collectionspace.services.blob.BlobsCommon;
\r
102 import org.collectionspace.services.blob.DimensionSubGroup;
\r
103 import org.collectionspace.services.blob.DimensionSubGroupList;
\r
104 import org.collectionspace.services.blob.MeasuredPartGroup;
\r
105 import org.collectionspace.services.blob.MeasuredPartGroupList;
\r
106 //import org.collectionspace.services.blob.BlobsCommonList;
\r
107 //import org.collectionspace.services.blob.BlobsCommonList.BlobListItem;
\r
108 import org.collectionspace.services.jaxb.BlobJAXBSchema;
\r
109 import org.collectionspace.services.nuxeo.client.java.CommonList;
\r
110 import org.collectionspace.services.nuxeo.extension.thumbnail.ThumbnailConstants;
\r
111 import org.collectionspace.services.common.blob.BlobOutput;
\r
113 import org.collectionspace.services.config.service.ListResultField;
\r
115 //import org.collectionspace.ecm.platform.quote.api.QuoteManager;
\r
117 // TODO: Auto-generated Javadoc
\r
119 * The Class NuxeoImageUtils.
\r
121 public class NuxeoImageUtils {
\r
122 /** The Constant logger. */
\r
123 private static final Logger logger = LoggerFactory
\r
124 .getLogger(NuxeoImageUtils.class);
\r
126 private static final String MIME_JPEG = "image/jpeg";
\r
128 * FIXME: REM - These constants should be coming from configuration and NOT
\r
131 public static final String DERIVATIVE_ORIGINAL = "Original";
\r
132 public static final String DERIVATIVE_ORIGINAL_TAG = DERIVATIVE_ORIGINAL
\r
135 public static final String DERIVATIVE_ORIGINAL_JPEG = "OriginalJpeg";
\r
136 public static final String DERIVATIVE_ORIGINAL_JPEG_TAG = DERIVATIVE_ORIGINAL_JPEG
\r
139 public static final String DERIVATIVE_MEDIUM = "Medium";
\r
140 public static final String DERIVATIVE_MEDIUM_TAG = DERIVATIVE_MEDIUM + "_";
\r
142 public static final String DERIVATIVE_THUMBNAIL = "Thumbnail";
\r
143 public static final String DERIVATIVE_THUMBNAIL_TAG = DERIVATIVE_THUMBNAIL
\r
146 public static final String DERIVATIVE_UNKNOWN = "_UNKNOWN_DERIVATIVE_NAME_";
\r
149 // Image Dimension fields
\r
151 public static final String PART_IMAGE = "digitalImage";
\r
152 public static final String PART_SUMMARY = "The dimensions of a digital image -width, height, and pixel depth.";
\r
153 public static final String WIDTH = "width";
\r
154 public static final String HEIGHT = "height";
\r
155 public static final String DEPTH = "depth";
\r
156 public static final String UNIT_PIXELS = "pixels";
\r
157 public static final String UNIT_BITS = "bits";
\r
159 // Image Metadata schemas - These are Nuxeo defined schemas
\r
161 public static final String SCHEMA_IPTC = "iptc";
\r
162 public static final String SCHEMA_IMAGE_METADATA = "image_metadata";
\r
164 private static final int THUMB_SIZE_HEIGHT = 100;
\r
166 private static final int THUMB_SIZE_WIDTH = 75;
\r
168 // static DefaultBinaryManager binaryManager = new DefaultBinaryManager();
\r
169 // //can we get this from Nuxeo? i.e.,
\r
170 // Framework.getService(BinaryManger.class)
\r
172 // /** The temp file name. */
\r
173 // static String tempFileName = "sunset.jpg";
\r
175 // /** The file separator. */
\r
176 // static String fileSeparator = System.getProperty("file.separator");
\r
178 // /** The cur dir. */
\r
179 // static String curDir = System.getProperty("user.dir");
\r
182 * Instantiates a new nuxeo image utils.
\r
184 NuxeoImageUtils() {
\r
185 // empty constructor
\r
189 * Use this for debugging Nuxeo's PictureView class
\r
191 private static String toStringPictureView(PictureView pictureView) {
\r
192 StringBuffer strBuffer = new StringBuffer();
\r
193 strBuffer.append("Description: " + pictureView.getDescription() + '\n');
\r
194 strBuffer.append("FileName: " + pictureView.getFilename() + '\n');
\r
195 strBuffer.append("Height: " + pictureView.getHeight() + '\n');
\r
196 strBuffer.append("Width: " + pictureView.getWidth() + '\n');
\r
197 strBuffer.append("Tag: " + pictureView.getTag() + '\n');
\r
198 strBuffer.append("Title: " + pictureView.getTitle() + '\n');
\r
199 return strBuffer.toString();
\r
202 // FIXME: REM - This needs to be configuration-bases and NOT hard coded!
\r
203 // FIXME: REM - Use MultiviewPicture adapter to get some of this information
\r
204 static private String getDerivativeUri(String uri, String derivativeName) {
\r
205 String result = DERIVATIVE_UNKNOWN;
\r
207 if (derivativeName.startsWith(DERIVATIVE_ORIGINAL_TAG) == true) {
\r
208 result = DERIVATIVE_ORIGINAL;
\r
209 } else if (derivativeName.startsWith(DERIVATIVE_ORIGINAL_JPEG_TAG) == true) {
\r
210 result = DERIVATIVE_ORIGINAL_JPEG;
\r
211 } else if (derivativeName.startsWith(DERIVATIVE_MEDIUM_TAG) == true) {
\r
212 result = DERIVATIVE_MEDIUM;
\r
213 } else if (derivativeName.startsWith(DERIVATIVE_THUMBNAIL_TAG) == true) {
\r
214 result = DERIVATIVE_THUMBNAIL;
\r
217 return uri + result + "/" + BlobInput.URI_CONTENT_PATH;
\r
220 static private HashMap<String, Object> createBlobListItem(Blob blob,
\r
222 HashMap<String, Object> item = new HashMap<String, Object>();
\r
224 String value = blob.getEncoding();
\r
225 if (value != null && !value.trim().isEmpty()) {
\r
226 item.put(BlobJAXBSchema.encoding, value);
\r
228 value = Long.toString(blob.getLength());
\r
229 if (value != null && !value.trim().isEmpty()) {
\r
230 item.put(BlobJAXBSchema.length, value);
\r
232 value = blob.getMimeType();
\r
233 if (value != null && !value.trim().isEmpty()) {
\r
234 item.put(BlobJAXBSchema.mimeType, value);
\r
236 value = blob.getFilename();
\r
237 if (value != null && !value.trim().isEmpty()) {
\r
238 item.put(BlobJAXBSchema.name, value);
\r
240 value = getDerivativeUri(uri, blob.getFilename());
\r
241 if (value != null && !value.trim().isEmpty()) {
\r
242 item.put(BlobJAXBSchema.uri, value);
\r
248 static public CommonList getBlobDerivatives(RepositoryInstance repoSession,
\r
249 String repositoryId, List<ListResultField> resultsFields, String uri)
\r
251 CommonList commonList = new CommonList();
\r
252 int nFields = resultsFields.size() + 2;
\r
253 String fields[] = new String[nFields];// FIXME: REM - Patrick needs to fix this hack. It is a "common list" issue
\r
254 fields[0] = "csid";
\r
256 for (int i = 2; i < nFields; i++) {
\r
257 ListResultField field = resultsFields.get(i - 2);
\r
258 fields[i] = field.getElement();
\r
260 commonList.setFieldsReturned(fields);
\r
262 IdRef documentRef = new IdRef(repositoryId);
\r
263 DocumentModel documentModel = repoSession.getDocument(documentRef);
\r
264 DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel
\r
265 .getAdapter(BlobHolder.class);
\r
266 List<Blob> docBlobs = docBlobHolder.getBlobs();
\r
267 // List<BlobListItem> blobListItems = result.getBlobListItem();
\r
268 HashMap<String, Object> item = null;
\r
269 for (Blob blob : docBlobs) {
\r
270 item = createBlobListItem(blob, uri);
\r
271 commonList.addItem(item);
\r
278 * [dublincore, uid, picture, iptc, common, image_metadata]
\r
280 static private Map<String, Object> getMetadata(Blob nuxeoBlob)
\r
282 ImagingService service = Framework.getService(ImagingService.class);
\r
283 Map<String, Object> metadataMap = service.getImageMetadata(nuxeoBlob);
\r
284 return metadataMap;
\r
287 private static String[] imageTypes = {"jpeg", "bmp", "gif", "png", "tiff", "octet-stream"};
\r
288 static private boolean isImageMedia(Blob nuxeoBlob) {
\r
289 boolean result = false;
\r
291 String mimeType = nuxeoBlob.getMimeType().toLowerCase().trim();
\r
292 String[] parts = mimeType.split("/"); // split strings like "application/xml" into an array of two strings
\r
293 if (parts.length == 2) {
\r
294 for (String type : imageTypes) {
\r
295 if (parts[1].equalsIgnoreCase(type)) {
\r
305 static private MeasuredPartGroupList getDimensions(
\r
306 DocumentModel documentModel, Blob nuxeoBlob) {
\r
307 MeasuredPartGroupList result = null;
\r
309 if (isImageMedia(nuxeoBlob) == true) try {
\r
310 ImagingService service = Framework.getService(ImagingService.class);
\r
311 ImageInfo imageInfo = service.getImageInfo(nuxeoBlob);
\r
312 Map<String, Object> metadataMap = getMetadata(nuxeoBlob);
\r
314 if (imageInfo != null) {
\r
316 // Create a timestamp to add to all the image's dimensions
\r
318 String valueDate = GregorianCalendarDateTimeUtils
\r
321 result = new MeasuredPartGroupList();
\r
322 List<MeasuredPartGroup> measuredPartGroupList =
\r
323 (result).getMeasuredPartGroup();
\r
325 // Create a new measured part for the "image"
\r
327 MeasuredPartGroup mpGroup = new MeasuredPartGroup();
\r
328 mpGroup.setMeasuredPart(PART_IMAGE);
\r
329 mpGroup.setDimensionSummary(PART_SUMMARY);
\r
330 mpGroup.setDimensionSubGroupList(new DimensionSubGroupList());
\r
331 List<DimensionSubGroup> dimensionSubGroupList = mpGroup.getDimensionSubGroupList()
\r
332 .getDimensionSubGroup();
\r
337 DimensionSubGroup widthDimension = new DimensionSubGroup();
\r
338 widthDimension.setDimension(WIDTH);
\r
339 widthDimension.setMeasurementUnit(UNIT_PIXELS);
\r
340 widthDimension.setValue(intToBigDecimal(imageInfo.getWidth()));
\r
341 widthDimension.setValueDate(valueDate);
\r
342 dimensionSubGroupList.add(widthDimension);
\r
346 DimensionSubGroup heightDimension = new DimensionSubGroup();
\r
347 heightDimension.setDimension(HEIGHT);
\r
348 heightDimension.setMeasurementUnit(UNIT_PIXELS);
\r
350 .setValue(intToBigDecimal(imageInfo.getHeight()));
\r
351 heightDimension.setValueDate(valueDate);
\r
352 dimensionSubGroupList.add(heightDimension);
\r
356 DimensionSubGroup depthDimension = new DimensionSubGroup();
\r
357 depthDimension.setDimension(DEPTH);
\r
358 depthDimension.setMeasurementUnit(UNIT_BITS);
\r
359 depthDimension.setValue(intToBigDecimal(imageInfo.getDepth()));
\r
360 depthDimension.setValueDate(valueDate);
\r
361 dimensionSubGroupList.add(depthDimension);
\r
363 // Now set out result
\r
365 measuredPartGroupList.add(mpGroup);
\r
367 if (logger.isWarnEnabled() == true) {
\r
368 logger.warn("Could not synthesize a dimension list of the blob: "
\r
369 + documentModel.getName());
\r
372 } catch (Exception e) {
\r
373 logger.warn("Could not extract image information for blob: "
\r
374 + documentModel.getName(), e);
\r
380 // FIXME: Add error checking here, as none of these calls return an
\r
382 static private BigDecimal intToBigDecimal(int i) {
\r
383 BigInteger bigint = BigInteger.valueOf(i);
\r
384 BigDecimal bigdec = new BigDecimal(bigint);
\r
388 static private BlobsCommon createBlobsCommon(DocumentModel documentModel,
\r
390 BlobsCommon result = new BlobsCommon();
\r
392 if (documentModel != null) {
\r
393 result.setMimeType(nuxeoBlob.getMimeType());
\r
394 result.setName(nuxeoBlob.getFilename());
\r
395 result.setLength(Long.toString(nuxeoBlob.getLength()));
\r
396 result.setRepositoryId(documentModel.getId());
\r
397 MeasuredPartGroupList measuredPartGroupList = getDimensions(
\r
398 documentModel, nuxeoBlob);
\r
399 if (measuredPartGroupList != null) {
\r
400 result.setMeasuredPartGroupList(measuredPartGroupList);
\r
403 // Check to see if a thumbnail preview was created by Nuxeo
\r
404 if (documentModel.hasFacet(ThumbnailConstants.THUMBNAIL_FACET)) {
\r
405 String errorMsg = null;
\r
406 String thumbnailName = null;
\r
408 thumbnailName = (String)documentModel.getProperty(ThumbnailConstants.THUMBNAIL_SCHEMA_NAME,
\r
409 ThumbnailConstants.THUMBNAIL_FILENAME_PROPERTY_NAME);
\r
410 Blob thumbnailBlob = (Blob)documentModel.getProperty(ThumbnailConstants.THUMBNAIL_SCHEMA_NAME,
\r
411 ThumbnailConstants.THUMBNAIL_PROPERTY_NAME);
\r
412 } catch (ClientException e) {
\r
413 errorMsg = "Could not extract the name of the thumbnail preview image file.";
\r
414 if (logger.isTraceEnabled()) {
\r
415 logger.trace(errorMsg, e);
\r
419 if (errorMsg == null) {
\r
420 logger.info("A thumbnail preview was created for this document blob: " + thumbnailName);
\r
422 logger.warn(errorMsg);
\r
431 * This is a prototype method that is not currently used as of 1/1/2012. However,
\r
432 * it may be useful now that we've transitioned to using an embedded Nuxeo server.
\r
434 static private File getBlobFile(RepositoryInstance ri,
\r
435 DocumentModel documentModel, Blob blob) {
\r
436 DefaultBinaryManager binaryManager = null;
\r
437 RepositoryDescriptor descriptor = null;
\r
441 RepositoryService repositoryService1 = (RepositoryService) Framework
\r
442 .getRuntime().getComponent(RepositoryService.NAME);
\r
444 String repositoryName = documentModel.getRepositoryName();
\r
445 RepositoryManager repositoryManager = repositoryService1
\r
446 .getRepositoryManager();
\r
447 descriptor = repositoryManager.getDescriptor(repositoryName);
\r
449 // Keep this code around for future work/enhancements
\r
450 // binaryManager = new DefaultBinaryManager();
\r
452 // File storageDir = binaryManager.getStorageDir();
\r
453 // // SQLBlob blob = (SQLBlob)
\r
454 // // doc.getPropertyValue("schema:blobField");
\r
455 // File file = binaryManager.getFileForDigest(blob.getDigest(), false);
\r
457 } catch (Exception e) {
\r
458 e.printStackTrace();
\r
461 // Keep this code around for future work/enhancements
\r
463 // binaryManager.initialize(SQLRepository.getDescriptor(descriptor));
\r
464 // } catch (IOException e) {
\r
465 // // TODO Auto-generated catch block
\r
466 // e.printStackTrace();
\r
467 // } catch (Exception e) {
\r
468 // // TODO Auto-generated catch block
\r
469 // e.printStackTrace();
\r
472 // Keep this code around for future work/enhancements
\r
473 // File storageDir = binaryManager.getStorageDir();
\r
474 // SQLBlob blob = (SQLBlob)
\r
475 // documentModel.getPropertyValue("schema:blobField");
\r
476 // File file = binaryManager.getFileForDigest(blob.getDigest(), false);
\r
482 * Returns a schema, given the name of a schema. Possibly usefule in the future
\r
484 * @param schemaName
\r
486 * @return a schema.
\r
488 private static Schema getSchemaFromName(String schemaName) {
\r
489 SchemaManager schemaManager = null;
\r
491 schemaManager = Framework.getService(SchemaManager.class);
\r
492 } catch (Exception e) {
\r
493 // TODO Auto-generated catch block
\r
494 e.printStackTrace();
\r
496 return schemaManager != null ? schemaManager.getSchema(schemaName)
\r
501 * Gets the blob. Not in use now, but might be useful in the future.
\r
503 * @param nuxeoSession
\r
504 * the nuxeo session
\r
509 static private Blob getBlob(RepositoryInstance nuxeoSession, String id) {
\r
510 Blob result = null;
\r
513 Repository repository = nuxeoSession.getRepository();
\r
514 // binaryManager.initialize(new RepositoryDescriptor());
\r
515 // binaryManager.getBinary("a4cac052ae0281979f2dcf5ab2e61a6c");
\r
516 // DocumentResolver.resolveReference(nuxeoSession, documentRef);
\r
517 // binaryManager = repository.getBinaryManager();
\r
518 // documentModel.getr
\r
519 } catch (Exception x) {
\r
520 x.printStackTrace();
\r
527 * Gets the type service. Not in use, but please keep for future reference
\r
529 * @return the type service
\r
530 * @throws ClientException
\r
531 * the client exception
\r
533 private static TypeManager getTypeService() throws ClientException {
\r
534 TypeManager typeService = null;
\r
536 typeService = Framework.getService(TypeManager.class);
\r
537 } catch (Exception e) {
\r
538 throw new ClientException(e);
\r
540 return typeService;
\r
548 * @return the bytes
\r
550 private static byte[] getBytes(InputStream fis) {
\r
551 ByteArrayOutputStream bos = new ByteArrayOutputStream();
\r
552 byte[] buf = new byte[128 * 1024];
\r
554 for (int readNum; (readNum = fis.read(buf)) != -1;) {
\r
555 bos.write(buf, 0, readNum);
\r
556 // no doubt here is 0
\r
558 * Writes len bytes from the specified byte array starting at
\r
559 * offset off to this byte array output stream.
\r
561 System.out.println("read " + readNum + " bytes,");
\r
563 } catch (IOException ex) {
\r
564 logger.error(ex.getMessage(), ex);
\r
566 byte[] bytes = bos.toByteArray();
\r
567 // bytes is the ByteArray we need
\r
572 * Creates the serializable blob. We may need this code, do not remove.
\r
574 * @param fileInputStream
\r
575 * the file input stream
\r
582 private static Blob createSerializableBlob(InputStream fileInputStream,
\r
583 String filename, String mimeType) {
\r
586 // persisting the blob makes it possible to read the binary content
\r
587 // of the request stream several times (mimetype sniffing, digest
\r
588 // computation, core binary storage)
\r
589 byte[] bytes = getBytes(fileInputStream);
\r
590 blob = new ByteArrayBlob(bytes);
\r
592 if (filename != null) {
\r
593 filename = getCleanFileName(filename);
\r
595 blob.setFilename(filename);
\r
596 // mimetype detection
\r
597 MimetypeRegistry mimeService = Framework
\r
598 .getService(MimetypeRegistry.class);
\r
599 String detectedMimeType = mimeService
\r
600 .getMimetypeFromFilenameAndBlobWithDefault(filename, blob,
\r
602 if (detectedMimeType == null) {
\r
603 if (mimeType != null) {
\r
604 detectedMimeType = mimeType;
\r
607 detectedMimeType = "application/octet-stream";
\r
610 blob.setMimeType(detectedMimeType);
\r
611 } catch (MimetypeDetectionException e) {
\r
612 logger.error(String.format("could not fetch mimetype for file %s",
\r
614 } catch (Exception e) {
\r
615 logger.error("", e);
\r
621 * Creates a serializable blob from a stream, with filename and mimetype
\r
625 * Creates an in-memory blob if data is under 64K, otherwise constructs a
\r
626 * serializable FileBlob which stores data in a temporary file on the hard
\r
631 * the input stream holding data
\r
633 * the file name. Will be set on the blob and will used for
\r
634 * mimetype detection.
\r
636 * the detected mimetype at upload. Can be null. Will be verified
\r
637 * by the mimetype service.
\r
640 private static Blob createStreamingBlob(File file, String filename,
\r
644 // persisting the blob makes it possible to read the binary content
\r
645 // of the request stream several times (mimetype sniffing, digest
\r
646 // computation, core binary storage)
\r
647 blob = StreamingBlob.createFromFile(file, mimeType).persist();
\r
649 if (filename != null) {
\r
650 filename = getCleanFileName(filename);
\r
652 blob.setFilename(filename);
\r
653 // mimetype detection
\r
654 MimetypeRegistry mimeService = Framework
\r
655 .getService(MimetypeRegistry.class);
\r
656 String detectedMimeType = mimeService
\r
657 .getMimetypeFromFilenameAndBlobWithDefault(filename, blob,
\r
659 if (detectedMimeType == null) {
\r
660 if (mimeType != null) {
\r
661 detectedMimeType = mimeType;
\r
664 detectedMimeType = "application/octet-stream";
\r
667 blob.setMimeType(detectedMimeType);
\r
668 } catch (MimetypeDetectionException e) {
\r
669 logger.error(String.format("could not fetch mimetype for file %s",
\r
671 } catch (IOException e) {
\r
672 logger.error("", e);
\r
673 } catch (Exception e) {
\r
674 logger.error("", e);
\r
679 private static Blob createNuxeoFileBasedBlob(File file) {
\r
680 Blob result = null;
\r
682 result = new FileBlob(file);
\r
687 * Returns a clean filename, stripping upload path on client side.
\r
694 * @return the clean file name
\r
696 private static String getCleanFileName(String filename) {
\r
698 int lastWinSeparator = filename.lastIndexOf('\\');
\r
699 int lastUnixSeparator = filename.lastIndexOf('/');
\r
700 int lastSeparator = Math.max(lastWinSeparator, lastUnixSeparator);
\r
701 if (lastSeparator != -1) {
\r
702 res = filename.substring(lastSeparator + 1, filename.length());
\r
710 * Gets Nuxeo's file manager service.
\r
712 * @return the file manager service
\r
713 * @throws ClientException
\r
714 * the client exception
\r
716 private static FileManager getFileManagerService() throws ClientException {
\r
717 FileManager result = null;
\r
719 result = Framework.getService(FileManager.class);
\r
720 } catch (Exception e) {
\r
721 String msg = "Unable to get Nuxeo's FileManager service.";
\r
722 logger.error(msg, e);
\r
723 throw new ClientException("msg", e);
\r
729 * Creates the picture.
\r
733 * @param repoSession
\r
737 * @return the string
\r
738 * @throws Exception
\r
740 public static BlobsCommon createBlobInRepository(ServiceContext ctx,
\r
741 RepositoryInstance repoSession, BlobInput blobInput) throws Exception {
\r
742 BlobsCommon result = null;
\r
745 File blobFile = blobInput.getBlobFile();
\r
746 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
\r
747 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
\r
748 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
\r
750 if (logger.isTraceEnabled()) {
\r
751 for (String facet : wspaceDoc.getFacets()) {
\r
752 logger.trace("Facet: " + facet);
\r
754 for (String docType : wspaceDoc.getDocumentType().getChildrenTypes()) {
\r
755 logger.trace("Child type: " + docType);
\r
759 result = createBlobInRepository(repoSession, wspaceDoc, blobFile, null /*mime type*/);
\r
760 } catch (Exception e) {
\r
761 logger.error("Could not create image blob", e);
\r
769 * Creates the image blob.
\r
771 * @param nuxeoSession
\r
772 * the nuxeo session
\r
773 * @param blobLocation
\r
774 * the blob location
\r
781 * @return the string
\r
783 static public BlobsCommon createBlobInRepository(RepositoryInstance nuxeoSession,
\r
784 DocumentModel blobLocation,
\r
785 // InputStream file,
\r
786 File file, String mimeType) {
\r
787 BlobsCommon result = null;
\r
790 // Blob fileBlob = createStreamingBlob(blobFile, blobFile.getName(),
\r
792 Blob fileBlob = createNuxeoFileBasedBlob(file);
\r
793 String digestAlgorithm = getFileManagerService()
\r
794 .getDigestAlgorithm(); // Only call this because we seem to need some way of initializing Nuxeo's FileManager with a call.
\r
796 logger.debug("Start --> Starting call to Nuxeo to create the blob document."); // For example, see Nuxeo's DefaultPictureAdapter class for details
\r
797 DocumentModel documentModel = getFileManagerService()
\r
798 .createDocumentFromBlob(nuxeoSession, fileBlob,
\r
799 blobLocation.getPathAsString(), true,
\r
801 logger.debug("Stop --> Finished calling Nuxeo to create the blob document.");
\r
803 result = createBlobsCommon(documentModel, fileBlob); // Now create our metadata resource document
\r
804 } catch (Exception e) {
\r
806 logger.error("Could not create new Nuxeo blob document.", e); //FIXME: REM - This should probably be re-throwing the exception?
\r
813 // * This is an alternate approach to getting information about an image
\r
814 // * and its corresponding derivatives.
\r
816 // // MultiviewPictureAdapter multiviewPictureAdapter =
\r
817 // documentModel.getAdapter(MultiviewPictureAdapter.class);
\r
818 // MultiviewPictureAdapterFactory multiviewPictureAdapterFactory = new
\r
819 // MultiviewPictureAdapterFactory();
\r
820 // MultiviewPictureAdapter multiviewPictureAdapter =
\r
821 // (MultiviewPictureAdapter)multiviewPictureAdapterFactory.getAdapter(documentModel,
\r
823 // if (multiviewPictureAdapter != null) {
\r
824 // PictureView[] pictureViewArray = multiviewPictureAdapter.getViews();
\r
825 // for (PictureView pictureView : pictureViewArray) {
\r
826 // if (logger.isDebugEnabled() == true) {
\r
827 // logger.debug("-------------------------------------");
\r
828 // logger.debug(toStringPictureView(pictureView));
\r
836 * @param repoSession
\r
838 * @param repositoryId
\r
839 * the repository id
\r
840 * @param derivativeTerm
\r
841 * the derivative term
\r
842 * @return the image
\r
844 static public BlobOutput getBlobOutput(ServiceContext ctx,
\r
845 RepositoryInstance repoSession,
\r
846 String repositoryId,
\r
847 String derivativeTerm,
\r
848 Boolean getContentFlag,
\r
849 StringBuffer outMimeType) {
\r
850 BlobOutput result = new BlobOutput();
\r
851 boolean isNonImageDerivative = false;
\r
853 if (repositoryId != null && repositoryId.isEmpty() == false)
\r
855 IdRef documentRef = new IdRef(repositoryId);
\r
856 DocumentModel documentModel = repoSession.getDocument(documentRef);
\r
858 Blob docBlob = null;
\r
859 DocumentBlobHolder docBlobHolder = (DocumentBlobHolder) documentModel
\r
860 .getAdapter(BlobHolder.class);
\r
861 if (docBlobHolder instanceof PictureBlobHolder) {
\r
862 // if it is a PictureDocument then it has these
\r
863 // Nuxeo schemas: [dublincore, uid, picture, iptc, common, image_metadata]
\r
865 // Need to add the "MultiviewPictureAdapter" support here to
\r
866 // get the view data, see above.
\r
868 PictureBlobHolder pictureBlobHolder = (PictureBlobHolder) docBlobHolder;
\r
869 if (derivativeTerm != null) {
\r
870 docBlob = pictureBlobHolder.getBlob(derivativeTerm);
\r
871 // Nuxeo derivatives are all JPEG
\r
872 outMimeType.append(MIME_JPEG);
\r
874 docBlob = pictureBlobHolder.getBlob();
\r
877 docBlob = docBlobHolder.getBlob();
\r
878 if (derivativeTerm != null) { // If its a derivative request on a non-image blob, then return just a document image thumnail
\r
879 isNonImageDerivative = true;
\r
884 // Create the result instance that will contain the blob metadata
\r
885 // and an InputStream with the bits if the 'getContentFlag' is
\r
888 BlobsCommon blobsCommon = createBlobsCommon(documentModel, docBlob);
\r
889 result.setBlobsCommon(blobsCommon);
\r
890 if (getContentFlag == true) {
\r
891 InputStream remoteStream = null;
\r
892 if (isNonImageDerivative == false) {
\r
893 remoteStream = docBlob.getStream();
\r
895 remoteStream = NuxeoImageUtils.class.getClassLoader() // for now, non-image derivatives are just placeholder document images
\r
896 .getResourceAsStream("documentImage.jpg");
\r
897 outMimeType.append(MIME_JPEG);
\r
899 BufferedInputStream bufferedInputStream = new BufferedInputStream(
\r
900 remoteStream); // FIXME: REM - To improve performance, try
\r
901 // BufferedInputStream(InputStream in, int size)?
\r
902 result.setBlobInputStream(bufferedInputStream);
\r
904 } catch (Exception e) {
\r
905 if (logger.isErrorEnabled() == true) {
\r
906 logger.error(e.getMessage(), e);
\r
917 * Notes and code snippets about Nuxeo's support for binaries and image
\r
924 * MultiviewPictureAdapter org.nuxeo.ecm.platform.picture.api.adapters
\r
925 * PictureResourceAdapter pictureResourceAdapter = (PictureResourceAdapter)
\r
926 * documentModel.getAdapter(PictureResourceAdapter.class); String thumbnailPath
\r
927 * = pictureResourceAdapter.getViewXPath("Thumbnail");
\r
929 * Map<String,Serializable> blobHolderProps = docBlobHolder.getProperties();
\r
930 * String filePath = docBlobHolder.getFilePath(); List<Blob> docBlobs =
\r
931 * docBlobHolder.getBlobs();
\r
933 * stream = new FileInputStream(fileUploadHolder.getTempFile());
\r
935 * public String addFile(InputStream fileUpload, String fileName) fileName =
\r
936 * FileUtils.getCleanFileName(fileName); DocumentModel currentDocument =
\r
937 * navigationContext.getCurrentDocument(); String path =
\r
938 * currentDocument.getPathAsString(); Blob blob =
\r
939 * FileUtils.createSerializableBlob(fileUpload, fileName, null);
\r
941 * DocumentModel createdDoc = getFileManagerService().createDocumentFromBlob(
\r
942 * documentManager, blob, path, true, fileName);
\r
943 * eventManager.raiseEventsOnDocumentSelected(createdDoc);
\r
945 * protected FileManager fileManager;
\r
947 * protected FileManager getFileManagerService() throws ClientException { if
\r
948 * (fileManager == null) { try { fileManager =
\r
949 * Framework.getService(FileManager.class); } catch (Exception e) {
\r
950 * log.error("Unable to get FileManager service ", e); throw new
\r
951 * ClientException("Unable to get FileManager service ", e); } } return
\r
956 * RepositoryService repositoryService = (RepositoryService)
\r
957 * Framework.getRuntime().getComponent( RepositoryService.NAME);
\r
958 * RepositoryManager repositoryManager =
\r
959 * repositoryService.getRepositoryManager(); RepositoryDescriptor descriptor =
\r
960 * repositoryManager.getDescriptor(repositoryName); DefaultBinaryManager
\r
961 * binaryManager = new DefaultBinaryManager(
\r
962 * SQLRepository.getDescriptor(descriptor)));
\r
964 * File storageDir = binaryManager.getStorageDir(); SQLBlob blob = (SQLBlob)
\r
965 * doc.getPropertyValue("schema:blobField"); File file =
\r
966 * binaryManager.getFileForDigest( blob.getBinary().getDigest(), false);
\r
970 * RepositoryInstance.getStreamURI()
\r
972 * String getStreamURI(String blobPropertyId) throws ClientException
\r
974 * Returns an URI identifying the stream given the blob property id. This method
\r
975 * should be used by a client to download the data of a blob property.
\r
977 * The blob is fetched from the repository and the blob stream is registered
\r
978 * against the streaming service so the stream will be available remotely
\r
979 * through stream service API.
\r
981 * After the client has called this method, it will be able to download the
\r
982 * stream using streaming server API.
\r
984 * Returns: an URI identifying the remote stream Throws: ClientException
\r
988 * A blob contains usually large data.
\r
990 * Document fields holding Blob data are by default fetched in a lazy manner.
\r
992 * A Blob object hides the data source and it also describes data properties
\r
993 * like the encoding or mime-type.
\r
995 * The encoding is used to decode Unicode text content that was stored in an
\r
996 * encoded form. If not encoding is specified, the default java encoding is
\r
997 * used. The encoding is ignored for binary content.
\r
999 * When retrieving the content from a document, it will be returned as source
\r
1000 * content instead of returning the content bytes.
\r
1002 * The same is true when setting the content for a document: you set a content
\r
1003 * source and not directly the content bytes. Ex:
\r
1005 * File file = new File("/tmp/index.html"); FileBlob fb = new FileBlob(file);
\r
1006 * fb.setMimeType("text/html"); fb.setEncoding("UTF-8"); // this specifies that
\r
1007 * content bytes will be stored as UTF-8 document.setProperty("file", "content",
\r
1011 * Then you may want to retrieve the content as follow:
\r
1013 * Blob blob = document.getProperty("file:content"); htmlDoc = blob.getString();
\r
1014 * // the content is decoded from UTF-8 into a java string
\r