2

I am having difficulties setting up an endpoint with the services module to download a file. Ultimately I want the user to hit the services end point with something like /api/download/file/fid and it will download the file to the users machine. The reason I want to do it through services and not just get users to hit /sites/default/files/file.filetype is because I need to be able to restrict access to the download link with HTTP basic auth. I have tried searching but cannot find how to achieve this? any help is much appreciated.

EDIT: My xml response when I imported arpitr's endpoint configuration and called it.

<result>
    <fid>6</fid>
    <uid>1</uid>
    <filename>Transition1.jpeg</filename>
    <uri>public://Transition1.jpeg</uri>
    <filemime>image/jpeg</filemime>
    <filesize>435007</filesize>
    <status>1</status>
    <timestamp>1475199010</timestamp>
    <type>image</type>
    <uuid>4c703ad2-290c-4573-9a29-a7e86e1a874e</uuid>
    <field_file_image_alt_text/>
    <field_file_image_title_text/>
    <rdf_mapping/>
    <metadata>
        <height>2304</height>
        <width>3072</width>
    </metadata>
    <alt/>
    <title/>
    <height>2304</height>
    <width>3072</width>
</result>

I am missing <file> which is the content of the file base 64 encoded, any idea why this could be?

2

You can send an image through resources using base64_encode(), or _file_resource_retrieve().

I don't remember the specifics of it all, but here is how I did it for Services in a project last year. You'll have to dissect the code yourself, but it definitely does work:

/**
 * Override of the Services module File resource, retrieve method.
 *
  * The original method does not allow for the retrieval of an image with an image style.
  * This override accepts an additional parameter, $image_style, which if set will return
  * the file data for the image in that image style. This allows for the retrieval of private files.
  *
  * @param $image_style
  *   If this value is set to an image style, the data for the image in that image style will be returned.
  *   Otherwise, the original File resource is invoked.
  *
  * @see _file_resource_retrieve()
  */
 function ba_api_file_retrieve($fid, $include_file_contents, $get_image_style, $image_style)
 {
    if($image_style)
    {
        if ($file = file_load($fid))
        {
            $filepath = $file->uri;

            if($image_style == 'raw')
            {
                $return = base64_encode(file_get_contents(drupal_realpath($filepath)));
            }
            else
            {
                $return = base64_encode(file_get_contents(drupal_realpath(image_style_path($image_style, $filepath))));
            }
        }
        else
        {
            $return = 'file not found';
        }
    }
    else
    {
        module_load_include('inc', 'services', 'resources/file_resource');

        $return = _file_resource_retrieve($fid, $include_file_contents, $get_image_style);
    }

    return $return;
 }
| improve this answer | |
1

You can try importing the following exported working service endpoint which will allow you to download file by accessing /api/download/file/

    $endpoint = new stdClass();
    $endpoint->disabled = FALSE; /* Edit this to true to make a default endpoint disabled initially */
    $endpoint->api_version = 3;
    $endpoint->name = 'file_download';
    $endpoint->server = 'rest_server';
    $endpoint->path = 'api/download';
    $endpoint->authentication = array(
      'services' => 'services',
    );
    $endpoint->server_settings = array();
    $endpoint->resources = array(
      'file' => array(
        'operations' => array(
          'retrieve' => array(
            'enabled' => '1',
          ),
        ),
      ),
    );
    $endpoint->debug = 1;

Update:

As output you will receive the complete file object as xml and file will be embded to file attribute which is base64 encoded, now when you receive the response you can use the file metadata and to download use file attribute from response, base64 decode and then save it. Response xml from above service endpoint.

<?xml version="1.0" encoding="utf-8"?>
    <result>
      <fid>2</fid>
      <uid>1</uid>
      <filename>Screen Shot 2016-09-28 at 1.54.36 PM.png</filename>
      <uri>public://field/image/Screen Shot 2016-09-28 at 1.54.36 PM.png</uri>
      <filemime>image/png</filemime>
      <filesize>61202</filesize>
      <status>1</status>
      <timestamp>1475196925</timestamp>
      <rdf_mapping/>
      <uri_full>http://drupal.services.local/sites/default/files/field/image/Screen%20Shot%202016-09-28%20at%201.54.36%20PM.png</uri_full>
      <target_uri>field/image/Screen Shot 2016-09-28 at 1.54.36 PM.png</target_uri>
 <file>iVBORw0KGgoAAAANSUhEUgAAA1QAAAC1CAYAAABYkb7nAAAYKmlDQ1BJQ0MgUHJvZmlsZQAAWIWVWQk4Vd27X..</file>
      <image_styles/>
    </result>
| improve this answer | |
  • Thanks for this, I have imported this however when i call /api/download/file/6 for example I just get xml that says <result/>. Thats it?.. I am looking for something that when I call it will start downloading the file. – Andre Pearce Sep 30 '16 at 1:33
  • @AndrePearce check the updated answer – arpitr Sep 30 '16 at 2:05
  • I am missing that <file> in my xml? any idea why..? or how I can add it there? check my edit to the question. – Andre Pearce Sep 30 '16 at 3:12
1

Another option would be to use the "private" file mode for the file field, then implement hook_file_download to do your access check when the download URL is requested.

You could also add HTTP authentication protection to a particular folder in the files directory via .htaccess or your webserver config.

| improve this answer | |
  • where would I implement hook_file_download? – Andre Pearce Sep 30 '16 at 3:14
  • @AndrePearce that hook would go in a module. – David Thomas Sep 30 '16 at 3:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.