How to serve a remote file as a download with CakePHP 3? - php

I would like to download file from remote NAS server, I am not able to force download to client. I am using this function:
public function download(){
// Set IP and port
define("FTP_CONNECT_IP", "xxx");
define("CONNECT_PORT", "21");
// Set username and password
define("FTP_LOGIN_USER", "username");
define("FTP_LOGIN_PASS", "password");
$remote_file = 'ftp://' . FTP_LOGIN_USER . ':' . FTP_LOGIN_PASS . '#' . FTP_CONNECT_IP . '/' .'PathToFile.avi';
$response = $this->response->withFile($remote_file,['download' => true]);
return $response;
}
It starts read something but never browser asks me for download. Please What is wrong?

You cannot use Response::withFile() for remote files, it only works with local files.
If you want to serve remote files, then you either have to temporarily store them on your server, or build a proper download response on your own, using for example CakePHPs callback stream for the response body to output the data manually.
Here's a quick example (doesn't support range requests):
return $this->response
->withType(pathinfo($remote_file, \PATHINFO_EXTENSION))
->withDownload(basename($remote_file))
->withLength(filesize($remote_file))
->withBody(new \Cake\Http\CallbackStream(function () use ($remote_file) {
ob_end_flush();
ob_implicit_flush();
readfile($remote_file);
}));
See also
Cookbook > Request & Response Objects > Response > Setting the Body

Related

WP ALL Export - Php snippet works on one server but not another

Im using the wordpress plugin wp all export to export a csv file and send it via FTP. They plugin provider actually had a code snippet on their site to do exactly what I need The only thing I changed in their code was adding in my own FTP details
See code example below
function wpae_after_export( $export_id ) {
// Retrieve export object.
$export = new PMXE_Export_Record();
$export->getById($export_id);
// Check if "Secure Mode" is enabled in All Export > Settings.
$is_secure_export = PMXE_Plugin::getInstance()->getOption('secure');
// Retrieve file path when not using secure mode.
if ( !$is_secure_export) {
$filepath = get_attached_file($export->attch_id);
// Retrieve file path when using secure mode.
} else {
$filepath = wp_all_export_get_absolute_path($export->options['filepath']);
}
// Path to the export file.
$localfile = $filepath;
// File name of remote file (destination file name).
$remotefile = basename($filepath);
// Remote FTP server details.
// The 'path' is relative to the FTP user's login directory.
$ftp = array(
'server' => 'ftp URL',
'user' => 'ftp Password',
'pass' => 'ftp Username',
'path' => '/'
);
// Ensure username is formatted properly
$ftp['user'] = str_replace('#', '%40', $ftp['user']);
// Ensure password is formatted properly
$ftp['pass'] = str_replace(array('#','?','/','\\'), array('%23','%3F','%2F','%5C'), $ftp['pass']);
// Remote FTP URL.
$remoteurl = "ftp://{$ftp['user']}:{$ftp['pass']}#{$ftp['server']}{$ftp['path']}/{$remotefile}";
// Retrieve cURL object.
$ch = curl_init();
// Open export file.
$fp = fopen($localfile, "rb");
// Proceed if the local file was opened.
if ($fp) {
// Provide cURL the FTP URL.
curl_setopt($ch, CURLOPT_URL, $remoteurl);
// Prepare cURL for uploading files.
curl_setopt($ch, CURLOPT_UPLOAD, 1);
// Provide the export file to cURL.
curl_setopt($ch, CURLOPT_INFILE, $fp);
// Provide the file size to cURL.
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localfile));
// Start the file upload.
curl_exec($ch);
// If there is an error, write error number & message to PHP's error log.
if($errno = curl_errno($ch)) {
if (version_compare(phpversion(), '5.5.0', '>=')) {
// If PHP 5.5.0 or greater is used, use newer function for cURL error message.
$error_message = curl_strerror($errno);
} else {
// Otherwise, use legacy cURL error message function.
$error_message = curl_error($ch);
}
// Write error to PHP log.
error_log("cURL error ({$errno}): {$error_message}");
}
// Close the connection to remote server.
curl_close($ch);
} else {
// If export file could not be found, write to error log.
error_log("Could not find export file");
}
}
add_action('pmxe_after_export', 'wpae_after_export', 10, 1);
I first set this up on a hostgator dedicated server and it worked perfectly but when I tried to copy the exact same process into another server hosting via AWS I don't receive the file into my FTP.
I would assume there is some sort of configration within the cpanel on the AWS server but I cannot figure out what. I dont get any error message when running the export either so the only thing I can see is that the export runs to 100% quickly but still runs for about 2 mins before its complete.
I also checked with the FTP owner and confirmed there is nothing on their site blocking the file from one server but not the other.
Someone also suggested I ensure both servers have cURL installed which I did confirm.

Flysystem S3 remote file download always corrupted

I recently started using Flysystem in an existing application with the intention of abstracting the local and remote (specifically, S3) filesystems. Everything was working ok on my development environment, on which I successfully configured the LocalAdapter. However, I cannot get S3 file downloads to work. I'd like to point out that file uploads are working perfectly, given that I can successfully download the file by manually browsing the S3 bucket in the AWS management console. That being said, I will skip the code that initializes the $filesystem variable.
My application is using a PSR-7 approach. That is, the code below is inside a function that is passed an object of type Psr\Http\Message\ServerRequestInterface as first argument and an object of type Psr\Http\Message\ResponseInterface as the second. Given that the local filesystem works fine, I think it is safe to assume that the problem doesn't lie there.
This is the code:
<?php
$stream = new \Zend\Diactoros\Stream($filesystem->readStream($filename));
$filesize = $stream->getSize();
return $response
->withHeader('Content-Type', 'application/pdf')
->withHeader('Content-Transfer-Encoding', 'Binary')
->withHeader('Content-Description', 'File Transfer')
->withHeader('Pragma', 'public')
->withHeader('Expires', '0')
->withHeader('Cache-Control', 'must-revalidate')
->withHeader('Content-Length', "{$filesize}")
->withBody($stream);
When I dump the $stream variable and the $filesize variable the results are as expected. The remote file contents are successfully printed. However, the file download is always corrupted and the file size is always of 0 bytes.
I am assuming that Flysystem takes care of everything behind the scenes and that I don't have to manually download the file to a temp folder first, before serving it to the client.
Any clue to what could be the problem?
Update 1
I have also tried with the following code, without any luck. However, it continues to work locally:
use Zend\Diactoros\CallbackStream;
$stream = new CallbackStream(function() use ($filesystem, $filename) {
$resource = $filesystem->readStream($filename);
while (!feof($resource)) {
echo fread($resource, 1024);
}
fclose($resource);
return '';
});
and
use Zend\Diactoros\CallbackStream;
$stream = new CallbackStream(function() use ($filesystem, $filename) {
$resource = $filesystem->readStream($filename);
fpassthru($resource);
return '';
});
Removing the Content-Length header seems to solve the problem.
See https://github.com/thephpleague/flysystem/issues/543 for more details.

Getting Direct Link of File With Extension in Lumen

I am using Lumen and I have an audio file in my uploads/videos/. I want to return the URL of the exact path (like app.com/videos/one.mp4).
I have achieved same thing in a very bad-practiced way by putting /{fileName} in routes, giving /one.mp4 and by removing the extension from String as soon as I receive the data (one.mp4). This is a bad way because I'm using a plugin that only accepts direct links.
(So for example, when I take link from any mp4 link (with extension), it works, but when I try it with my method, it doesn't
I found this example for Laravel, however Lumen apparently doesn't accept ->where:
Route::get('file/{filename}', 'FileController#getFile')->where('filename', '^[^/]+$');
and
public function getFile($filename)
{
return response()->download(storage_path($filename), null, [], null);
}
But when I try it in Lumen, I receive:
Call to undefined method Laravel\Lumen\Application::where()
What is the way of setting a route that receives filename with extension as parameters and returns the mp4 file in directory. So basically, I want to hit url myapi.com/videos/two.mp4 and play two.mp4 as soon as I hit to the link.
(Please note that I store the filename in database without extension, just as two for two.mp4.)
Example link format: https://v.cdn.vine.co/r/videos/12B2B2092B1284614590713798656_4be40e9deb2.4.1.3029273185552527049.mp4
Update:
This below chunk plays the audio in Chrome but not the app, may it be because of the app itself rather than Lumen? But it's playing successfully the above Vine link.
$app->get('/player/{filename}', 'PlayerController#show')
public function show ($filename)
{
$this->playVid($filename, 'videos');
}
public function playVid($filename, $showType)
{
if (file_exists("../uploads/" . $showType . "/" . $filename)) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$type = finfo_file($finfo, "../uploads/" . $showType . "/" . $filename);
header("Content-Type: " . $type);
readfile("../uploads/" . $showType . "/" . $filename);
}
}
If so, what is the difference between my file & Vine file? When I open both of the videos in browser, right+click and check source, both videos have type="video/mp4"
More information on Question: https://stackoverflow.com/questions/34369823/same-video-from-my-api-url-doesnt-work-where-works-from-online-url-from-proj
Update: I realised my problem was caused because I was using readFile, but I wanted to use streaming. If you believe that's the problem, refer here - https://stackoverflow.com/a/34435905/4705339

php - generate time limited download link for files on another server

I put my files on my VPS and user can direct download all files. but I want to hide my actual file paths and make time limited download links. I googled it and find some solutions but most of them was for files that were on same server and some of them has some coding in VPS side, but i can't write any php code on my VPS because it doesn't support php.
also I try some script that works well but generated link wasn't resumable and didn't show file size until download finished. How can I solve these problems?
You could use mod_auth_token (http://code.google.com/p/mod-auth-token/) apache module, if you are running apache as web frontend.
This is how you can handle the PHP side of the token generation process:
<?php
// Settings to generate the URI
$secret = "secret string"; // Same as AuthTokenSecret
$protectedPath = "/downloads/"; // Same as AuthTokenPrefix
$ipLimitation = false; // Same as AuthTokenLimitByIp
$hexTime = dechex(time()); // Time in Hexadecimal
//$hexTime = dechex(time()+120); // Link available after 2 minutes
$fileName = "/file_to_protect.txt"; // The file to access
// Let's generate the token depending if we set AuthTokenLimitByIp
if ($ipLimitation) {
$token = md5($secret . $fileName . $hexTime . $_SERVER['REMOTE_ADDR']);
}
else {
$token = md5($secret . $fileName. $hexTime);
}
// We build the url
$url = $protectedPath . $token. "/" . $hexTime . $fileName;
echo $url;
?>
If you cant make changes to the actual downloadlinks they will stay available for download until they are deleted from the server.
Of course you can make a Script which encrypts the download URL based on the system time, but once the user calls them within time he gets the decrypted URL from the script.

How can I resume an upload to ftp from php

I have a php-script that uploads files to an ftp server from a location with a very low bandwith, low reliability connection. I am currently using the ftp functions of php.
Sometimes the connection drops in the middle of a transfer. Is there a way to later resume the upload? How can this be done?
Edit:
People misunderstood that this is happening from a browser. However, this is not the case. It is a php cli script. So it is about a local file being uploaded to ftp, no user interaction.
Try getting the remote file's size and then issuing the APPE ftp command with the difference. This will append to the file. See http://webmasterworld.com/forum88/4703.htm for an example. If you want real control over this, I recommend using PHP's cURL functions.
I think all the previous answers are wrong. The PHP manage the resume, for that FTP_AUTODESK must be activated, and you have to activate the FTP_AUTORESUME during upload. The code should be something like this :
$ftp_connect = ftp_connect($host, $port);
ftp_set_option ($ftp_connect, FTP_AUTOSEEK, TRUE);
$stream = fopen($local_file, 'r');
$upload = ftp_fput($ftp_connect, $file_on_ftp, $stream, FTP_BINARY, FTP_AUTORESUME);
fclose($stream);
ftp_(f)put has a $startpos parameter. You should be able to do what you need using this parameter. (starts the transfer from the file size on your server).
However I never used it, so I don't know about the reliability. You should try.
EDIT:
Another Example: by cballou
Look here. A very simple and good example.
You could do something like this:
<?php
$host = 'your.host.name';
$user = 'user';
$passwd = 'yourpasswd';
$ftp_stream = ftp_connect($host);
//EDIT
ftp_set_option($ftp_stream, FTP_AUTOSEEK, 1);
if ( ftp_login($ftp_stream,$user,$passwd) ){
//activate passive mode
//ftp_pasv($ftp_stream, TRUE);
$ret = ftp_nb_put($ftp_stream, $remotefile, $localfile, FTP_BINARY,FTP_AUTORESUME);
while ( FTP_MOREDATA == $ret ) {
// continue transfer
$ret = ftp_nb_continue($ftp_stream);
}
if (FTP_FINISHED !== $ret){
echo 'Failure occured on transfer ...';
}
}
else {
print "FAILURE while login ...";
}
//free
ftp_close($ftp_stream);
?>
are you meaning you are going to allow user resume upload from you web interface ?
is this suit you ?
http://www.webmasterworld.com/php/3931706.htm
just use the filezilla can done everything for you ...i mean the uploading process...
it will keep the data and reopen after close you will be able to continue process q...
for automate and tracking , simply use our way...which is git and capistrano
there is an option for php
try google it...

Categories