1

I am trying to list all the files from a certain directory in a remote SFTP server as download links so that the user can have the option of downloading those files. So far, I have been able to read and list all the files from a specific directory as download links, but when I try to actually download a file by right clicking and choosing save link as... I get a "Failed - No File" message. Picture of my results

<?php
$connection = ssh2_connect('url', 22);
ssh2_auth_password($connection, username, password);

$sftp = ssh2_sftp($connection);
$sftp_fd = intval($sftp);

if ($handle = opendir("ssh2.sftp://$sftp_fd/path/to/remote/dir/")) {

   while (($entry = readdir($handle)) !== false) {
      if ($entry == "." || $entry == "..") { continue; }
      echo '<a href="/path/to/remote/dir/' .$entry. '">' .$entry. '<br>'.'</a>';
   }
   closedir($handle);
}
?>     
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
jl1126
  • 21
  • 1
  • 6
  • Since you are allowing users to download the file on a browser, you can only do so if the files are present in `/var/www/html/` folder and you have to specify the complete url `http://serverip_or_domain/dir/file1` – Samir Selia Aug 25 '19 at 10:50
  • I am able to list all my files on /var/www/html/ as download links. I am just not able to download them. When I click on save link as... it "downloads" but it says "Failed - No File." – jl1126 Aug 25 '19 at 10:57
  • Use absolute path i.e. complete path in your anchor's `href` attribute. Example, `http://example.com/dir/file` or `http://serverIP/dir/file` – Samir Selia Aug 25 '19 at 10:58
  • *"I am able to list all my files on /var/www/html/"* - So what does your SFTP code have to do with your question? Either your files are present directly on the web server - then you do not need to use SFTP code to list them and you can create direct download links. Or the files are actually on the SFTP server - then you need to use SFTP to list them (what yo do), but then you cannot create direct download links, instead you must code SFTP download. – Martin Prikryl Aug 25 '19 at 11:00
  • Your code uses `/path/to/remote/dir/` on the SFTP server. Not some `/var/www/html/` folder (presumably on the web server), which you refer to in your comment. Be consistent, otherwise we all get confused. – Martin Prikryl Aug 25 '19 at 11:02
  • I was testing my code with /var/www/html/ ... but I have also tested it with an actual remote directory. I am able to get a list of download links, but when I do save link as... I download an empty file. I posted a picture of my results. – jl1126 Aug 25 '19 at 11:12

1 Answers1

0

Your link in the generated <a> tag points back to the web server, which does not contain the linked file.

What you need to do is to link to a PHP script, giving it a name of the file to download. The script will then download the file from an SFTP server and pass the downloaded file back to the user (to the webbrowser).

echo '<a href="download.php?file='.urlencode($entry).'">'.htmlspecialchars($entry).'</a>';

A very trivial version of the download.php script:

<?

header('Content-Type: application/octet-stream');

$connection = ssh2_connect('url', 22);
ssh2_auth_password($connection, username, password);

$sftp = ssh2_sftp($connection);
$sftp_fd = intval($sftp);

echo file_get_contents("ssh2.sftp://$sftp_fd/path/to/remote/dir/" . $_GET["file"]);

Though for a really correct solution, you should provide some HTTP headers related to the file, like Content-Length, Content-Type and Content-Disposition.

Also the above trivial example will first download whole file from the SFTP server to the webserver. And only then it will start streaming it to the user (webbrowser). What is a waste of time and also of a memory on the webserver (if the files are large enough).

See a similar (though FTP, not SFTP) question:
Download file via PHP script from FTP server to browser with Content-Length header without storing the file on the web server

You may also want to autodetect Content-Type, unless all your files are of the same type.


You can of course use URL rewrite to make the URLs prettier.
I.e. turn download.php?file=myfile.txt to for example download/myfile.txt.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • Will the above code only download one file, or all the files in a directory? – jl1126 Aug 26 '19 at 05:58
  • Why should it download all files in a directory? It downloads the file you click on. – Martin Prikryl Aug 26 '19 at 06:31
  • Ah, ok, sorry, got a little confused. So this code will list the files as download links, correct? – jl1126 Aug 26 '19 at 07:47
  • No! *Your code* lists files as download links (you only need to change the URLs to be like `download.php?file=filename`, instead of `/path/to/remote/dir/filename`, as shown in the first part of my answer. -- The other part of my answer, shows how to implement the `download.php`. Please read my answer carefully again. – Martin Prikryl Aug 26 '19 at 07:48
  • Ok, everything works fine now, but I'm having trouble with the names of the files now. Without Content-Disposition, the file name comes out as download. I added the Content-Disposition header but I can only name one file. How can I get the name of any file I download? – jl1126 Aug 26 '19 at 09:20
  • *"but I can only name one file"* - I do not understand, why? Did you use `$_GET["file"]` in the `Content-Disposition` header value? – Martin Prikryl Aug 26 '19 at 09:22
  • 1
    Ah, no, I didn't. I just did that and everything works well. Thanks a lot. And sorry for the confusion. – jl1126 Aug 26 '19 at 09:28