Retrieving Images in Java

1
Hi all, I'm building a custom request handler that retrieves an image. I've been able to get it working to the point where I can return the name of the image and other String values. Additionally, when I access the request handler from the browser, the below code doesn't return any errors and seems to work. But the file returned doesn't have any recognizable image extension and contains unrecognizable text, as if when you open an image in a text editor. How do I retrieve the image in a way that it can be viewed by the end user? Furthermore, how can I retrieve both the thumbnail and full version of the image? After referencing a couple of blog posts, this is the request handler I've been working with: class GetImage extends RequestHandler { @Override public void processRequest(IMxRuntimeRequest request, IMxRuntimeResponse response, String parameters) throws Exception { OutputStream outputStream = response.getOutputStream(); String[] params = parameters.split("/"); String imgGUID = params[0]; response.setContentType("application/octet-stream"); IContext context = Core.createSystemContext(); IMendixObject image = Core.retrieveXPathQuery(context, "//System.FileDocument[id='" + imgGUID + "']").get(0); InputStream inputStream = Core.getFileDocumentContent(context, image); IOUtils.copy(inputStream, outputStream); IOUtils.closeQuietly(outputStream); } } I'm retrieiving from System.FileDocument because System.Image is a specialization of it, and also because I was following this blog post on how exacty to get the file content.
asked
2 answers
4

Some hints:

Consider contenttype 'image/jpeg' (or another image type)

At least set

            response.setStatus(IMxRuntimeResponse.OK);

One request handler can only return one image unless you pass a parameter like

/image?type=thumbnail

read the parameter in the requesthandler.

Edit: Jasper is right about the security this snippet may help:

            String cookie = request.getCookie("XASSESSIONID");
            if (cookie == null ||  cookie.isEmpty()) {
                // not logged in, ready.
                response.setStatus(IMxRuntimeResponse.OK);
                return;
            }
            logger.debug("Css Getting cookie " + cookie);
            ISession session = null;

            if (cookie != null) {
                logger.debug("Cookie found, trying to find active Mendix session");
                for (ISession activeSession : Core.getActiveSessions()) {
                    if (activeSession.getId().toString().equals(cookie)) {
                        session = activeSession;
                        logger.debug("Active Mendix session found");
                    }
                }
            }
            // session not found in list of active sessions.
            if (session==null) {
                // not logged in, ready.
                response.setStatus(IMxRuntimeResponse.OK);
                return;
            }

Edit 2:

To get the correct image type use apache Tika and this function

public org.apache.tika.mime.MediaType getFileFormat(FileDocument fileDocument) throws IOException {
    InputStream is = Core.getFileDocumentContent(getContext(), fileDocument.getMendixObject());
    TikaConfig config = TikaConfig.getDefaultConfig();
    Detector detector = config.getDetector();

    TikaInputStream stream = TikaInputStream.get(is);

    Metadata metadata = new Metadata();
    metadata.add(Metadata.RESOURCE_NAME_KEY, fileDocument.getName());
    org.apache.tika.mime.MediaType mediaType = detector.detect(stream, metadata);
    return mediaType;

}

Take care of the fact that apache tika also contains Apache poi and this may cause conflicts with other appstore-modules. To be compatible with poi 3.10 (used in mendix excel module AFAIK) use tika 1.6

answered
3

Please be careful what you are doing with that request handler. The way you have programmed the code right now opens up your application and potentially creates a huge security gap!

Your request handler as written in your question does not require the user to be logged in and needs a GUID as input. A GUID is a sequential nr, so by enabling this anybody could just call your request handler with a nr and keep increasing it. Your Java would just return all the documents that are in your system.




To resolve this you can do two things. Require a session and use the user context to only retrieve the data he has access too.

The RequestHandler has a function: this.getSessionFromRequest() which can give you the session from the user that is logged in. Using that session you can then create a context to retrieve the FileDocuments. So that would be:

IContext context = this.getSessionFromRequest().createContext();

IMendixObject image = Core.retrieveXPathQuery(context, "//System.FileDocument[id='" + imgGUID + "']").get(0);


Or alternatively if you don't want to require the user to login create a separate entity for PublicFileDocuments and query on that entity only. That way you explicitly control what is being published (or add a boolean or enum or something on the entity to limit what is available).
You want to make sure that a hacker can't get access to exports or any other confidential information.



Specific to your question, I would recommend two things.

  1. As Chris mentions add the Response code 200-OK through using: response.setStatus(IMxRuntimeResponse.OK);

  2. Do not close the outputstream. That closes(ends) the response before it reaches the browser. The outputstream is closed when the connection with the browser closes.

answered