Laravel 5.5 app. I need to retrieve images of driver's licenses from Amazon S3 (already working) and then upload it to Stripe using their api for identity verification (not working).
Stripe's documents give this example:
\Stripe\Stripe::setApiKey(PLATFORM_SECRET_KEY);
\Stripe\FileUpload::create(
array(
"purpose" => "identity_document",
"file" => fopen('/path/to/a/file.jpg', 'r')
),
array("stripe_account" => CONNECTED_STRIPE_ACCOUNT_ID)
);
However, I am not retrieving my files using fopen().
When I retrieve my image from Amazon S3 (using my own custom methods), I end up with an instance of Intervention\Image -- essentially, Image::make($imageFromS3) -- and I don't know how to convert this to the equivalent of the call to fopen('/path/to/a/file.jpg', 'r'). I have tried the following:
$image->stream()
$image->stream()->__toString()
$image->stream('data-url')
$image->stream('data-url')->__toString()
I have also tried skipping intervention image and just using Laravel's storage retrieval, for example:
$image = Storage::disk('s3')->get('path/to/file.jpg');
All of these approaches result in getting an Invalid hash exception from Stripe.
What is the proper way to get a file from S3 and convert it to the equivalent of the fopen() call?
If the file on S3 is public, you could just pass the URL to Stripe:
\Stripe\FileUpload::create(
array(
"purpose" => "identity_document",
"file" => fopen(Storage::disk('s3')->url($file)),
),
array("stripe_account" => CONNECTED_STRIPE_ACCOUNT_ID)
);
Note that this requires allow_url_fopen to be turned on in your php.ini file.
If not, then you could grab the file from S3 first, write it to a temporary file, and then use the fopen() method that the Stripe documentation speaks of:
// Retrieve file from S3...
$image = Storage::disk('s3')->get($file);
// Create temporary file with image content...
$tmp = tmpfile();
fwrite($tmp, $image);
// Reset file pointer to first byte so that we can read from it from the beginning...
fseek($tmp, 0);
// Upload temporary file to S3...
\Stripe\FileUpload::create(
array(
"purpose" => "identity_document",
"file" => $tmp
),
array("stripe_account" => CONNECTED_STRIPE_ACCOUNT_ID)
);
// Close temporary file and remove it...
fclose($tmp);
See https://secure.php.net/manual/en/function.tmpfile.php for more information.
I am using AWS S3 to generate a createPresignedRequest() to a file. When the user clicks on the link it opens in a new window, I am wanting it to force download. Is there anything I can do to enable this feature. I have looked at the docs but I am not seeing what I need.
Here's my code to create the url:
$s3 = new S3Client([
'credentials'=>[
'key'=>$key,
'secret'=>$secret,
],
'region'=>'us-west-2',
'version'=>'2006-03-01',
]);
$cmd = $s3->getCommand('GetObject', [
'Bucket' => $buckname,
'Key' => $file,
]);
$request = $s3->createPresignedRequest($cmd, '+7 days');
$secureUrl = (string) $request->getUri();
echo $secureUrl; // When the user clicks on the link - how can I force download the file instead of open in a new window.
You can force a download when generating the pre-signed URL, by adding ResponseContentDisposition to the GetObject parameters.
'ResponseContentDisposition' => '<string>',
This directs S3 to set a Content-Disposition response header with the value of <string> when the signed URL is used. The object remains unchanged.
The value to use for '<string>' can be one of these:
'attachment'
'attachment; filename="some-filename.jpg"'
The second example will cause most browsers to save the file with the name you specified, instead of its name in S3. There is a single space after the ; after the word attachment.
When you do this, you'll notice that the signed URL now contains &response-content-dispositon=... which is mentioned in the S3 API Reference.
Alternately, you can set the Content-Disposition to the desired value when you upload the object to S3 and this will automatically be present on all downloads.
It depends on content-type set. If suppose content-type is jpeg or txt which browser can show; then it is shown in the browser.. If browser is not capable of viewing the content-type then it automatically downloads it for example exe file.
The problem I have is that I need the Content-Disposition: attachment header to be present on EVERY file that hits my bucket.
In Wordpress, I can just use .htaccess to cover the filetypes in question (videos), but those rules do not extend to my S3 downloads which browsers are simply trying to open, instead of download.
I need an automated/default solution, since I am not the only one that uploads these files (our staff uploads through Wordpress, and the uploads all are stored on our S3 bucket). So using Cloudberry or other browsers is not useful for this situation. I can't adjust the files on a per-file basis (the uploads are too frequent).
Is there a way to do this?
(Other information: I'm using the "Amazon S3 and Cloudfront" plugin on Wordpress that is responsible for linking the two together. Unfortunately, the site is not public, so I cannot link to it.)
Unfortunately there is no way to set this for an entire bucket in S3, and also Cloudfront can only set Cache-Headers
But you can set The Content-disposition parameter when uploading files to S3.
For existing files, you must change the Header, so loop through every object in the Bucket, and copy it to itself using the new headers.
All I can say now, please post the code that uploads the file to S3.
First, you need to locate the code that puts the object in the bucket.
You can use notepad++ to search for "putObject" within the php files of whatever plugin you are using.
An example code from another WP plugin that stores files to S3 is as follows:
$this->s3->putObject( array(
'Bucket' => $bucket,
'Key' => $file['name'],
'SourceFile' => $file['file'],
) );
Now, simply add ContentDisposition' => 'attachment' like so:
$this->s3->putObject( array(
'Bucket' => $bucket,
'Key' => $file['name'],
'SourceFile' => $file['file'],
'ContentDisposition' => 'attachment',
) );
Thats it :)
Yes, you can set default Content-Disposition header for your each and every upcoming uploading file in your S3 bucket using Bucket Explorer's Bucket Default feature.
For existing files, you can use Update Metadata option that update metadata on every file exist in your bucket in batch.
You just need to -
Select Key as : Content-Disposition
Add Value as : attachment;filename={$file_name_without_path_$$}
Then update metadata on the files.
See this page to set Content-Disposition on your file.
More references:
http://www.bucketexplorer.com/documentation/amazon-s3--metadata-http-header-bucket-default-metadata.html
http://www.bucketexplorer.com/documentation/amazon-s3--how-to-manage-http-headers-for-amazon-s3-objects.html
http://www.bucketexplorer.com/documentation/amazon-s3--metadata-http-header-update-custom-metadata.html
Thanks
According to the docs, contentType is optional and it will attempt to determine the correct mime-type based on the file extension. However, it never seemed to guess the mime-type and always defaults to application/octet-stream
Here's my code:
$s3 = new AmazonS3();
$opt = array( 'fileUpload' => $_FILES['file']['tmp_name'],
'storage' => Amazons3::STORAGE_REDUCED);
$r = $s3->create_object('mybucket', $_FILES['file']['name'], $opt);
Here's a screenshot of my AWS console:
How do you actually automatically set the proper Content Type without setting contentType option, or do you really have to set it manually?
Additional info: If I download a file from the console (that I originally uploaded through SDK) then upload it again using the console, the proper Content-Type is set (ex: image/gif for GIF files instead of application/octet-stream)
Try to add 'contentType' key to your options array when creating object and use 'body' instead of 'fileUpload'. For example:
$s3->create_object('MY_BUCKET', 'xml_file.xml', array(
'body' => '<xml>Valid xml content</xml>',
'contentType' => 'text/xml'
));
Successfully creates file with text/xml content type. As I know Content-Type isn't set automatically when uploading through SDK. But where's the problem to derminate mime type from PHP side and set the 'contentType' property?
I have just run into this situation where, when uploading some files, the Content-Type was not automatically detected. I couldn't figure out what the cause was until I stepped through the code watching the SDK execution. I discovered that the CFMimeTypes::get_mimetype() function, which is used to automatically determine the Content-Type, uses a case sensitive comparison to determine the MIME type. Because of this only lowercase extensions will match. If your file extension is upper or mixed case, then it will not match and fallback to the 'application/octet-stream' MIME type.
To fix change the line:
'fileUpload' => $_FILES['file']['tmp_name']
to
'fileUpload' => strtolower( $_FILES['file']['tmp_name'] )
I am using the SDK v 1.5.8.2, but there doesn't look to be any changes to this detection in the 1.5.9 or 1.5.10 releases that have both come out in the past week. I consider this to be a bug and will be filing it as such.
I am trying to use http://code.google.com/p/amazon-s3-php-class/ to force-dowload files from AWS S3. I have an mp3 that I want people to "play" or "download." By default the when you access the file directly on s3 it begins to play in the browser. I need to add an option to actually download. I have Googled and found came up with nothing. I conceptually know what needs to happen but don't know how to produce it php. I know I need to modify the headers to Content-Disposition: attachment. Any help would be greatly appreciated.
Thanks,
Michael
Amazon has now solved this problem and allows overriding of headers on a per-request basis with signed requests:
http://docs.amazonwebservices.com/AmazonS3/latest/API/index.html?RESTObjectGET.html
w00t!
The php scripts that have been mentioned so far will work ok, but the main downside is that every time a visitor on your site requests a file, your own servers will load it from the S3 and then relay that data to the browser. For low traffic sites, it's probably not a big deal, but for high traffic ones, you definitely want to avoid running everything through your own servers.
Luckily, there's a fairly straight-forward way to set your files to be forced to download from the S3. And you're exactly right - you just want to set the content-type and content-disposition (just setting content-disposition will work in some browsers, but setting both should work in all browsers).
This code is assuming that you're using the Amazon S3 PHP class from Undesigned:
<?php
// set S3 auth and get your bucket listing
// then loop through the bucket and copy each file over itself, replacing the "request headers":
S3::copyObject($bucketName, $filename, $bucketName, $filename, "public-read", array(), array("Content-Type" => "application/octet-stream", "Content-Disposition" => "attachment"));
?>
Now all your files will be forced to download. You may need to clear your cache to see the change. And obviously, don't do that on any file that you actually do want to be loaded "inline" in the browser.
The nice part with this solution is that applications that load media files directly (like let's say an mp3 player in Flash) don't care about the content-type or content-disposition, so you can still play your files in the browser and then link to download that same file. If the user already finished loading the file in flash, they'll most likely still have it in their cache, which means their download will be super quick and it won't even cost you any extra bandwidth charges from the S3.
Just add this to your file's metadata on s3:
Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
Just wanting to post a contribution to this, Alex Neth is correct on this reference, but i do not feel a link is sufficient information, using amazon's own AWS PHP SDK2. Below I've outlined a basic (untested) method for calling data this way, you can use either S3 Factory Method or AWS Service Builder to make the S3 Client.
<?php
// S3 Factory Method
/*use Aws\S3\S3Client;
$s3= S3Client::factory(array(
'key' => '<aws access key>',
'secret' => '<aws secret key>'
));*/
// OR AWS Service Builder
use Aws\Common\Aws;
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
// Now lets create our request object.
$command = $s3->getCommand('GetObject',array(
'Bucket' => 'your-bucket-name',
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
));
$url = $command->createPresignedUrl('+1 days');
?>
You can then use PHP's header("Location: $url"); in order to redirect the visitor to the MP3 file with a force download, this should prevent it from playing in the browser, Please note, i use ResponseContentType quite frequently but I've never used ResponseContentDisposition with AWS (it should work according to the docs).
Converting this sample into a function should be easy, you could even pass in $bucket, $key, $force_download as such
<?php
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
// Create a service builder using a configuration file
$aws = Aws::factory('/path/to/my_config.json');
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => 'keyname',
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="filename.mp3',
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
// Location redirection to an MP3 force downlaod
header("Location: ".gen_url("recordings","my-file.mp3",true));
// Location redirection to a MP3 that lets the browser decide what to do.
header("Location: ".gen_url("recordings","my-file.mp3"));
?>
WARNING, if you haven't figured it out, this requires the AWS PHP SDK 2 currently (April 7th 2014) found here http://aws.amazon.com/sdkforphp/ This code is mostly pseudo code and may require some additional tweaking to actually make work as i'm referencing this from memory.
So modify my example above to be like this
<?php
header('Content-Type: audio/mpeg');
header("Content-Disposition: attachment; filename={$_GET['file']};");
readfile("url to the file/{$_GET['file']}");
exit();
?>
Now you will want to put some validation in there so that you not giving the world access to every file you put on S3, but this should work.
If you are using a library like Tarzan AWS, you can add meta headers, that amazon will include when the file is retrieved. Check out the meta parameter in the update_object function here, for example:
http://tarzan-aws.com/docs/2.0/files/s3-class-php.html#AmazonS3.update_object
Also worth mentioning is you are able to hard-set the headers for files in S3. For example, if you need to force-download a certain file you can set the appropriate headers for that file. Such as defaulting to stream, and either having a secondary file set to force-download or spend the bandwidth to use php/fputs and force-download via PHP.
<?php
require_once __DIR__.'/vendor/autoload.php';
use Aws\Common\Aws;
function gen_url($bucket,$key,$force_download=false){
// OR AWS Service Builder (personal method to do this)
$config = array(
'key' => '',
'secret' => '',
);
// Create a service builder using a configuration file
$aws = Aws::factory($config);
// Get the client from the builder by namespace
$s3 = $aws->get('S3');
$params = array(
'Bucket' => $bucket,
'Key' => $key,
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="'.$key,
);
if($force_download){
$params['ResponseContentType'] = 'application/octet-stream';
$params['ResponseContentDisposition'] = 'attachment; filename="'.basename($key).'"';
}
$command = $s3->getCommand('GetObject',$params);
return $command->createPresignedUrl('+1 days');
}
$bucket = '';
$filename = '';
$url = gen_url($bucket,$filename,true);
echo "\n".$url."\n\n";
The code above works, you just need to install the S3 composer dependency and link in the autoload file, put your key/secret into config and then supply the bucket/filename.
php would just download the file to the server, not the client. remember, php doesn't do anything on the client, it just returns content (html, javascript, css, xml, whatever...)
[edit: added for clarity]: php can serve audio content, but you want to serve a web page and audio at the same time. To get that client behaviour, you have to get the client to request the file based on the web page's html or javascript.
So you have to get the client to download the file. For instance, have an iframe on the page with the url of the file on s3. Use css to make the iframe invisible. It should render the page and download and play the mp3.
Otherwise, look into using javascript to kick of a download when the page loads. I'm not sure if that's possible.
This is now possible by overwriting the S3 headers using signed requests.
Request Parameters
There are times when you want to override certain response header values in a GET response. For example, you might override the Content-Disposition response header value in your GET request.
You can override values for a set of response headers using the query parameters listed in the following table.
response-content-type - Sets the Content-Type header of the response
response-content-disposition - Sets the Content-Disposition header of the response.
Note
You must sign the request, either using an Authorization header or a pre-signed URL, when using these parameters. They cannot be used with an unsigned (anonymous) request.
So, you would set those headers to:
response-content-disposition: attachment; filename=FILENAME.EXT
response-content-type: application/octet-stream
Found answer here: https://stackoverflow.com/a/5903710/922522
<?php
// PHP solution (for OP and others), works with public and private files
// Download url expires after 30 minutes (no experation after the download initiates, for large files)
// ***Forces client download***
$signed_url = $s3_client->getObjectUrl($s3_bucket_name, $s3_filename_key, '+30 minutes', array(
'ResponseContentType' => 'application/octet-stream',
'ResponseContentDisposition' => 'attachment; filename="your-file-name-here.mp4"'
));
redirect($signed_url);
I never tried Amazon's S3 hosting, but don't you have access to using .htaccess files there? Then you can set Content-Type and Content-Disposition for an entire directory with this entry:
<IfModule mod_headers.c>
<FilesMatch "\.(mp3)$">
ForceType audio/mpeg
Header set Content-Disposition attachment
</FilesMatch>
</IfModule>