how do I force a download from API?

Long story short, I have an API that creates a temporary Zip file and I want to force it to download.

I am sure that I'm missing something. APIs are new to me.
The API works, it creates the file ok, I just can't force it to download (prompt to download would also be ok) .

This is how I got the Zip to download in a v6.x entry point called from a SugarWidget. How do I do this in v7?
($filename is the path to the file relative to sugar root).






       
   header("Pragma: public");
   header("Cache-Control: maxage=1, post-check=0, pre-check=0");
   header("Content-Type: application/force-download");
   header("Content-type: application/octet-stream");
   header("Content-Disposition: attachment; filename=\"".$display_filename."\";");
   // disable content type sniffing in MSIE
   header("X-Content-Type-Options: nosniff");
   header("Content-Length: " . filesize($filename));
   header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + 2592000));
   set_time_limit(0);
   @ob_end_clean();
   ob_start();
   readfile($filename);
   @ob_flush();
   @unlink($filename);
                           


thanks,
FrancescaS

  • You may try to extend the idea from "clients/base/api/FileTempApi.php" which actually use the download_file.php in function getTempImage().



  • This is working for me:

    <?php

    class zipFilesApi extends SugarApi

    {

      public function registerApiRest() {

        return array(

          'zipFiles' => array(

            'reqType' => 'GET',

            'path' => array('<module>','zipFiles','relatedToModule','?','modelId','?','selection','?'),

            'pathVars' => array('module','','','related_module','','model_id','','selection'),

            'method' => 'zipFiles',

            'shortHelp' => 'CustomWR: Zip files related to module/model_id. Use selection to specify all or list ids of files to zip',

            'longHelp' => '',

          ),

        );

      }

      function zipFiles($api, $args)

      {

    //    require_once('custom/entry_points/Utils/SafeZipArchive.php');

        $GLOBALS['log']->debug("ZipFiles::STARTS NOW");

        $GLOBALS['log']->debug(print_r($args, true));

        global $sugar_config;

        $site_url = $sugar_config['site_url'];

        $upload_dir = rtrim($sugar_config['upload_dir'],"/");

        $related_module = $args['related_module'];

        $document_module = $args['module'];

        $id = $args['model_id'];

        $all = ($args['selection']=='all')?'1':'0';

        $files=array();

        $GLOBALS['log']->debug("Download Attachments:: Related to module = {$related_module}; id = {$id}; all={$all}; relationship={$relationship}");

        $m= BeanFactory::retrieveBean($related_module, $id); //get the parent bean

        if ($related_module == 'Cases'){

          $prefix = "Case_{$m->case_number}_";

        }else{

          $prefix = $related_module . '_';

        }

        if($all == '1'){

          //get all documents related to this parent

          require_once('custom/entry_points/Utils/getRelationshipByModules.php');

          list($relationship, $mod) = getRelationshipByModules ($related_module, $document_module);

          $m->load_relationship("{$relationship}");

          foreach($m->$relationship->getBeans() as $file){

            $f = new $document_module;

            $f->retrieve($file->id);

            $files[$f->id] = $f->filename;

          }

        }else{

          //get the id and filenames of files checked

          $file_ids = explode(',', ltrim($args['selection'],','));

          foreach($file_ids as $file_id){

            $f = new $document_module;

            $f->retrieve($file_id);

            $files[$f->id] = $f->filename;

          }

        }

        if (!empty($files)) {

          $GLOBALS['log']->debug("Attachments to zip " . print_r($files,true));

          // open zip

          $time = time();

          $zip_path = "tmp/{$prefix}download_{$time}.zip";

          $GLOBALS['log']->debug($zip_path);

          $zip = new ZipArchive();

          if($zip->open($zip_path, ZIPARCHIVE::CREATE)){

            $arrLocalNames = array();

            foreach ($files as $strFilePath => $strLocalName) {

              $strFilePath = "{$upload_dir}/{$strFilePath}";

              while( $arrLocalNames[ $strLocalName ] > 0 ) {

                $arrPathInfo = pathinfo( $strLocalName );

                $intNext = $arrLocalNames[ $strLocalName ]++;

                $strLocalName = "$arrPathInfo[filename]($intNext).$arrPathInfo[extension]";

              }

              // Add to the count.

              $arrLocalNames[ $strLocalName ]++;

              // Add the file safely.

              $zip->addFile( $strFilePath, $strLocalName );

            }

            $zip->close();

            return($sugar_config['site_url'].'/'.$zip_path);

          }

        }

      }

    }

  • How was this API called in the JS side ? Can I know how the return URL is used for downloading.

  • In a custom field for a rowaction

    custom/clients/base/fields/customdownload/customdownload.js

     

    this was added to a documents-style custom module. In list view/subpanel I have checkboxes that can be used to select multiple files hence the option "downloadSelected" which groups them inside a zipped folder.

    ({
      extendsFrom: 'RowactionField',
      initialize: function(options) {
        this._super("initialize", [options]);
        this.type = 'rowaction';
      },
      hasAccess: function(){
        return true;
      },
      plugins: ['File'],
      events: {
        'click a[name=customdownloadall]': 'downloadAll',
        'click a[name=customdownloadselected]': 'downloadSelected'
      },
      startDownload: function(uri) {
        app.api.fileDownload(uri, {
          error: function(data) {
            app.error.handleHttpError(data, {});
          }
        }, {iframe: this.$el});
      },
      downloadSelected: function(){
        var self = this,
            document_module = this.module;
            module_name = this.context.parent.get('module'),
            model_id = this.context.parent.get('model').get('id');
            if(typeof(this.context.get('selected_attachments')) != 'undefined'){
              selection = Object.keys(this.context.get('selected_attachments')).join();
            }else{
              app.alert.show("no-selection", {
                level: 'info',
                messages: 'No files selected, please use the checkbox next to the file name to select',
                autoClose: 'false',
              });
              return;
            }
        var  url = app.api.buildURL(document_module+'/zipFiles/relatedToModule/'+module_name+'/modelId/'+model_id+'/selection/'+selection);
        //make the api call to create the zip file to be downloaded 
        app.api.call('GET', url, null, {
          success: function(o){
            //if successfully created, the output is the full url of the file
            //download the file
            self.startDownload(o);
          },
          error: function(e){
            app.alert.show("download-error", {
              level: 'info',
              messages: 'Error Downloading file/s, please contact your SugarCRM Administrator',
              autoClose: 'false',
            });
          },
        });
      },
      downloadAll: function(){
        var self = this,
            document_module = this.module;
            module_name = this.context.parent.get('module'),
            model_id = this.context.parent.get('model').get('id'),
            selection = 'all',
            url = app.api.buildURL(document_module+'/zipFiles/relatedToModule/'+module_name+'/modelId/'+model_id+'/selection/'+selection);
        //make the api call to create the zip file to be downloaded 
        app.api.call('GET', url, null, {
          success: function(o){
            //if successfully created, the output is the full url of the file
            //download the file
            self.startDownload(o);
          },
          error: function(e){
            app.alert.show("download-error", {
              level: 'info',
              messages: 'Error Downloading file/s, please contact your SugarCRM Administrator',
              autoClose: 'false',
            });
          },
        });
      },
    })