skip to Main Content

I have

a download button when clicking on it, it takes about 15 seconds to download a file because it has to SFTP into the server, find the right path/files, and return response download.

enter image description here

HTML

<a class="btn btn-primary btn-sm text-primary btn-download-1" onclick="startDownload('1')"><i class="fa fa-download "></i></a>

Note : where 1 is the key for me to know which file it is …


Right now

the button just will trigger this function below

function startDownload(interfaceId) {
    window.location = "/nodes/interface/capture/download?port=" + interfaceId;
    console.log(interfaceId);
}

It basically refreshes the page and call that download route

/nodes/interface/capture/download

public function download_files()
{

    $dir = '';
    $portNumber = Request::get('port');
    $zipMe = false;

    $remotePath = "/home/john/logs/".$dir."/";

    if (!isset($dir) || $dir == null) {
        return redirect()->back()->withInput()->withFlashDanger('SFTP Could not connect.');
    }

    $acsIp =  explode('://', env('ACS_URL'));
    $acsIp =  explode(':',$acsIp[1])[0];
    $sftp = new SFTP($acsIp.':22');

    if (!$sftp->login('john', '***')) {
        return redirect()->back()->withInput()->withFlashDanger('SFTP Could not connect.');
    }


    // Get into the Specified Directory
    $sftpConn = Storage::disk('sftp');

    $SFTPFiles = $sftpConn->allFiles('/'.$dir);

    if ( count($SFTPFiles) > 0 ) {
        foreach ($SFTPFiles as $file) {
            $fileName = $file;
            break;
        }

    } else {
        Log::info('Files Not found in the Remote!');
        return redirect()->back()->withInput()->withFlashDanger('Files Not found in the Remote!');
    }

    // Create and give 777 permission to remote-files directory
    if (!is_dir(public_path('remote-files/'.$dir))) {
        mkdir(public_path('remote-files/'.$dir), 0777, true);
    }

    $filesToZip = [];

    foreach ( $SFTPFiles as $fileName ) {
        if ( $fileName == '..' || $fileName == '.' ) {
            continue;
        } else if ( $fileName == '' ) {
            Log::info('File not found');
            continue;
        }

        $fileName     = explode("/", $fileName);
        $onlyFileName = (!empty($fileName) && isset($fileName[1])) ? $fileName[1] : "";
        $filepath = $remotePath.$onlyFileName;

        if (strpos($onlyFileName , $portNumber) !== false) {


            // Download the remote file at specified location in Local
            if (!$sftp->get($filepath, 'remote-files/'.$dir.'/'.$onlyFileName))
            {
                die("Error downloading file ".$filepath);
            }

            $file = public_path('remote-files/'.$dir.'/').$onlyFileName;

            $headers = array(
                'Content-Description: File Transfer',
                'Content-Type: application/octet-stream',
                'Content-Disposition: attachment; filename="'.basename($file).'"',
                'Cache-Control: must-revalidate',
                'Pragma: public',
                'Content-Length: ' . filesize($file)
            );

            return Response::download($file, $onlyFileName, $headers);

        }

        // IF File is exists in Directory
        if ( file_exists( public_path('remote-files/'.$dir.'/').$onlyFileName ) ) {

            $filesToZip[] = public_path('remote-files/'.$dir.'/').$onlyFileName;
            Log::info('File Generated '.'remote-files/'.$dir.'/'.$onlyFileName);


            // Remove Files from public/remote-files
            $this->removeDirAndFiles('', public_path('remote-files/'.$dir));
            exit;


        } else {
            Log::info('File not Generated '.'remote-files/'.$dir.'/'.$onlyFileName);
        }
    }
}

Result

It is working, but somehow, it stays there for 15 seconds without any sort of feedback. It’s really bad, users have no idea what is going on.

I want to show a modal say "Downloading is in progress, please don’t close the window", but I don’t know how to do that since it is mandatory for my to use a GET to download a file.I’m kind of stuck now.

Any suggestions for me?

2

Answers


  1. Since the delay is at the server side, I believe you can do two ways:

    1. Have a route to create queue jobs and determine what is the progress, and another route for user to download a downloaded files in server. The user will ping to the first route every second to determine status.

      The idea is to generate a unique ID each time a session is created, store it in an Array that is shared by both download_files() route and another to initiate download_files() and store its result.

      Example:

      $global_progress = array();
      $global_files = array();
      
      public function checkup_progress($id = null) {
          if ($id == null) {
              // this is new request, create job here
              $id = generate_id_func();
              download_file_job($id);
          } else if ($global_progress[$id] != "FINISHED") {
              // return $global_progress[$id] result
          } else {
              // finished, return download link
              // return route to "link_to_download_file/$id"
          }
      }
      
      public function download_file_job($id) {
          $global_progress[$id] = "NEW";
          // some code
          $global_progress[$id] = "IN PROGRESS (1)";
          // more code
          // more state here
          $global_files[$id] = $file;
          $global_progress[$id] = "FINISHED";
      }
      
      public function link_to_download_file($id) {
          // code here
          return Response::download($file, $onlyFileName, $headers);
      }
      
    2. If you dont want to change anything, you can use websocket that will update download state after few operations, and send files to the client. But this would be restricted by the filesize as websocket are handled in javascript, which means download must be stored javascript object in browser memory first.

    Login or Signup to reply.
  2. Show an overlay div that has a message of your choosing, before doing the redirect. This requires nothing to be done on the server side.

    function startDownload(interfaceId) {
        document.getElementById("overlay").style.display = "block"; // Show the overlay
        window.location = "/nodes/interface/capture/download?port=" + interfaceId;
        console.log(interfaceId);
    }
    

    CSS

    #overlay {
      position: fixed; /* Sit on top of the page content */
      display: none; /* Hidden by default */
      width: 100%; /* Full width (cover the whole page) */
      height: 100%; /* Full height (cover the whole page) */
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: rgba(0,0,0,0.5); /* Black background with opacity */
      z-index: 2; /* Specify a stack order in case you're using a different order for other elements */
      cursor: pointer; /* Add a pointer on hover */
      display: flex;
      justify-content: center;
      align-items: center;
      color: yellow;
    }
    

    HTML

    <div id="overlay">
      Downloading is in progress, please don't close the window
    </div> 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search