Web services sending files

Hello everyone!

Recently I've started to develop plugins for my own social network. The project in which work is to develop a mobile app that serves as a social network interface. So I am using the feature of web services to establish the link.

I have implemented part of the plugin with the functions needed for communication. I use the library Retrofit (from Android) for HTTP queries. And for user authentication, I use tokens. I request the token with the function 'auth.gettoken' during login activity, and then requests that do add them '?auth_token=xxxxxxxxxxx' to the url. Up to this point everything works properly.

My problem arises when requesting from the mobile application content style images or files. At first I thought to use the URL of the object (getIconUrl), and in the case of the images of the user profile work because the URL is public but in the case of the images of the ElggFile not type (although you add '?auth_token=xxxxxxxxxxx' to the url). When I try to get these images or files can not reach them, the url redirects me to the base url (login).

How I can access these images or files?

  • You can send/receive files as raw data encoded with base64. See here for inspiration: https://github.com/hypeJunction/hypeGraph/blob/master/classes/hypeJunction/Graph/Controllers/File.php
    I should have code for webservices but can't find it now.

  • It sounds like you want a URL that serves the file, like http://example.com/file/download/1234/?auth_token=... but of course this doesn't currently work.

    I think you'd have to take the code from this view, put it in a function and make an API endpoint for it. It would be a little hacky as it would have to accept a GET request and wouldn't return an API response, just the image header and data.

  • I developed the following function to the api ...

    elgg_ws_expose_function(
        // API FUNCTION
        "getimage",
        // Internal function
        "format_ElggMedia",
        // Parameters
        array(),
        // Description
        'Provides one image',
        // Type of call
        'GET',
        // API authentication required
        false,
        // User authentication required
        false
    );
    
    ...
    
    function format_ElggMedia() {
        // get a file
        $river_items = elgg_get_entities(
            array('type' => 'object',
                  'subtype' => 'file',
                  'limit' => 1)
        );
    
        foreach ($river_items as $media) {
            $mime = $media->getMimeType();
            $filename = $media->originalfilename;
    
            header("Pragma: public");
            header("Content-type: $mime");
            header("Content-Disposition: attachment; filename=\"$filename\"");
            header("Content-Length: {$media->getSize()}");
    
            while(ob_get_level()) {
                ob_end_clean();
            }
    
            flush();
            readfile($media->getFilenameOnFilestore());
        }
    }

    When I do the query in the browser, this returns me the file...but in the mobile application I only receive the following json response:

    {"status": -1, "result": "format_ElggMedia doesn't return any value"}

    I still do not get the file... :$$

  • Finally I got it.

    I have coded the file in base64 and have included in the json response object.

    function format_ElggMedia($media) {
        
        if ( !($media instanceof ElggEntity) ) return null;
        
        $return = array(
            'guid' => $media->getGUID(),
            'filename' => $media->originalfilename,
            'content' => base64_encode(file_get_contents($media->getFilenameOnFilestore())),
        );
    
        return $return;
    }
    
  • I also highly recommend to require at least API authentication for ALL web service methods.

    Instead of checking ElggEntity instance, check ElggFile, since you are calling getFilenameOnFilestore. You may also want to include the mimetype of the file and it's length to verify that you decoded it correctly on the other end. Ideally, you would also include an md5 checksum.