About FirstSpirit-UploadHooks (Developer-API)

andre
I'm new here
3 6 2,886

1. UploadHooks in general

...here we go ...brief.....

* ... processed on new media creation or changing a picture/file of a FirstSpirit medium

  (yes, FirstSpirit-5 does support setting preview thumbnails for media of type FirstSpirit.File)

* ... process before and/or after an upload to the FirstSpirit-Server was triggered by the user or a FirstSpirit-API-Call

  @see implementation interface: http://www.e-spirit.com/odfs50/dev/?de/espirit/firstspirit/service/mediamanagement/UploadHook.html

  or an upload has been aborted by an UploadHook throwing an UploadRejectedException in this case the Hooks uploadAborted method will be called

  to give the Hook a chance of cleaning something.

some use cases out of many...

* call any thirdparty- or custom FirstSpirit-Module-Component -service e.g scan for virus before uploading a medium to the FirstSpirit-Server

* generate custom thumbnails e.g. for PDF Files

* extract meta data information (e.g. image exif data) and assign/write them to the meta data of a FirstSpirit media.

  for this case FirstSpirit provides a deffault ExifUploadHook implementation sprinkled with a small exif-developer-api

  for exif data processing and formatting able to extract any simple

  (e.g. image orientation '...extract image exif orientation information and rotate the image auto-magically' , timestamps ...) exif types

  as well complex type like Geolocation information.

  Dev-API:

  @see http://www.e-spirit.com/odfs50/dev/de/espirit/firstspirit/server/mediamanagement/exif/package-frame....

  in addition to the default ExifUploadHook-Implementation it is possible to register custom additional ExifHook(s)

  @see http://www.e-spirit.com/odfs50/dev/?de/espirit/firstspirit/service/mediamanagement/ExifUploadHook.ht...

  @see Release notes chapter 7.7 for some FirstSpirit exif documentation - usages in templates and basic concepts

      http://www.e-spirit.com/odfs50/en/weiterfuehrende_themen/exif_daten/exif_daten_1.html?community

* reject uploads to the FirstSpirit-Server for any desired cases throwing an UploadRejectedException (this needs to be done in the pre*-Method of the Hook)

2. ... some details - an example PDF-Thumbnail-Hook implementation (use PDFRenderer library - Source License: LGPL-2.1)

* registering the hook as FirstSpirit-Module (module descriptor)

-------Module descriptor ------------------------------------------------------------------------------------------

<module>

    <name>FirstSpirit PDF Preview-Image UploadHook Example</name>

    <version>@VERSION@</version>

    <description>FirstSpirit PDF Preview-Image UploadHook example implementation.</description>

    <vendor>e-Spirit AG</vendor>

    <components>

        <public>

            <name>PDF_THUMBNAIL_UPLOADHOOK</name>

            <description>FirstSpirit PDF Preview-Image UploadHook example implementation.</description>

            <class>de.espirit.firstspirit.opt.example.PdfThumbnailHookExample</class>

            <resources>

                <resource scope="server">lib/fs-example_pdfthumbnailhook.jar</resource>

                <resource scope="server">lib/PDFRenderer-0.9.0.jar</resource>

            </resources>

        </public>

    </components>

</module>

-----------------------------------------------------------------------------------------------------------------

...at least some java

---------------PdfThumbnailHookExample.java ---------------------------------------------------------------------

public class PdfThumbnailHookExample implements UploadHook {

    public void preProcess(@NotNull final BaseContext baseContext, @NotNull final Media media, @NotNull final File file, @NotNull final InputStream inputStream, final long length) throws UploadRejectedException, IOException {

        //nothing to do

    }

    public void preProcess(@NotNull final BaseContext baseContext, @NotNull final Media media, @NotNull final Picture picture, final Resolution resolution, @NotNull final InputStream inputStream, final long length) throws UploadRejectedException, IOException {

        //nothing to do

    }

    public void postProcess(@NotNull final BaseContext baseContext, @NotNull final Media media, @NotNull final File file, final long length) {

        final MimeType mimeType = file.getMimeType();

        if (mimeType == null) {

            return;

        }

        final String extension = file.getExtension();

        if (extension != null && !"pdf".equals(extension.toLowerCase(Locale.ENGLISH))) {

            return;

        }

        InputStream inputStream = null;

        try {

            inputStream = file.getInputStream();

            final Image image = createImage(baseContext, inputStream, (int) file.getSize());

            if (image == null) {

                baseContext.logWarning("PDF image generation failed.");

            }

            final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

            ImageIO.write((RenderedImage) image, "png", byteArrayOutputStream);

            final byte[] bytes = byteArrayOutputStream.toByteArray();

            if (bytes.length > 0) {

                file.setPreviewImage(bytes);

            }

        } catch (IOException e) {

            baseContext.logError("PDF image generation failed.", e);

        } finally {

            if (inputStream != null) {

                try {

                    inputStream.close();

                } catch (IOException ignore) {

                }

            }

        }

    }

    public void postProcess(@NotNull final BaseContext baseContext, @NotNull final Media media, @NotNull final Picture picture, final long length) {

        //nothing to do

    }

    public void uploadAborted(@NotNull final BaseContext baseContext, @NotNull final Media media, @NotNull final MediaElement mediaElement) {

        //nothing to do

    }

    @Nullable

    private static Image createImage(final BaseContext baseContext, final InputStream stream, final int size) throws IOException {

        final byte[] bytes = readFromStream(stream, size);

        final ByteBuffer buffer = ByteBuffer.allocateDirect(size);

        buffer.put(bytes);

        final PDFFile pdfFile = new PDFFile(buffer);

        final int pages = pdfFile.getNumPages();

        if (pages < 1) {

            baseContext.logWarning("Minimum of one pdf page is required to render an image.");

            return null;

        }

        final PDFPage page = pdfFile.getPage(0);

        return page.getImage((int) page.getWidth(), (int) page.getHeight(), page.getBBox(), null, true, true);

    }

    private static byte[] readFromStream(final InputStream in, final int size) throws IOException {

        final byte[] bytes = new byte[size];

        try {

            int offset = 0;

            do {

                final int len = in.read(bytes, offset, bytes.length - offset);

                if (len == -1) {

                    throw new IllegalStateException("buffer underflow");

                }

                offset += len;

            } while (offset < bytes.length);

        } finally {

            in.close();

        }

        return bytes;

    }

}

not much ...isn't it?

----------------------------------------------------------------------------------------------------

The PdfThumbnailHookExample.java captures always the first page of the pdf,

may be providing some settings (...which pdf page to render) through the media meta data could be useful.

feel free to ask your e-Spirit contact to get the full example hook implementation.

There's also an exif_example_project.tar.gz which you could ask our help desk for.

NOTE: keep in mind an UploadHook may run in the FirstSpirit JavaClient and FirstSpirit WebEdit.

6 Comments
kscheuing
I'm new here

Hallo Andre,

ist es irgendwie möglich den Scope des Upload Hooks zu begrenzen ohne jedes mal das Projekt abzufragen ?

Grüße, Kai

andre
I'm new here

hallo kai,

...der UploadHook wird bei upload vom medien aufgerufen und medien sind ja im prinzip projektgebunden....was genau meinst du mit "projekt abfragen"? bzw. wie und warum fragst du vom hook das projekt an? was ist denn der use-case?

--

andre

kscheuing
I'm new here

Hallo Andre, ich möchte die funktionalität des Upload Hooks nur in einem bestimmten Projekt verwenden. Wenn sich nun aber mehr wie ein Projekt auf meinem Server befindet funktioniert der Upload Hook ja in den anderen Projekten genauso. Das ist unter umständen nicht erwünscht.

Mit "Projekt abfragen" meine ich eine simple Verzweigung in der abgefragt wird in welches Projekt "geuploadet" wird.

Grüße, Kai

andre
I'm new here

ah, ok, ...nein ,das geht leider nicht. evtl. stellst du einen Feature-REquest: ProjectBasedUploadHook o.ä.

--

gruesse

andre

daniel_philippi
Occasional Collector

Hallo André.

Genial! Von dieser Art Hook bräuchte es in FirstSpirit noch viel mehr!!!!!

Der Hook greift beim Neuanlegen eines Bildes und beim Anlegen eines Bildausschnittes.

Letzteres wollte ich mir zu nutzen machen. Wenn der Redakteur einen Bildeausschnitt anlegt, wollte ich mit Hilfe der Crop Daten die Koordinaten des Rechtecks auslesen und weiterverarbeiten.

Nun ist es jedoch so, dass nach Klick auf "Übernehmen" im Bildausschnittdialog der Hook angesprochen wird. Ich bekomme dann die Höhe und Breite des Zuschnittes, aber nicht die Crop Daten da diese Daten vermutlich zu diesem Zeitpunkt noch nicht erstellt sind.

Gibt es irgend einen Weg wie ich doch an die Crop Daten kommen kann?

Eigentlich bräuchte ich ja nur den linken oberen Punkt des Ausschnittes. Den Rest bekomme ich über die Höhe und Breite. Kriege ich diesen Punkt vielleicht wo anders noch her?

Gruß,

Daniel

king
I'm new here

Hallo André,

gibt es eine Möglichkeit im Upload-Hook selbst, in Abhängigkeit vom auslösenden Context, eine Behandlung auszuführen? Etwa um sicherzustellen, dass nur im Fall eines Aufrufs aus dem ContentCreator das implementierte Upload-Hook-Verhalten anzustoßen.

Etwa:

...

if (baseContext.is(BaseContext.Env.WEBEDIT)))

...

Im Moment zieht das nicht, da der Hook selbst nicht im WEBEDIT-Context läuft! Nach meinem Verständnis wäre auch dafür wieder ein FeatureRequest notwendig. Richtig?

Version history
Last update:
‎02-08-2013 07:54 AM
Updated by: