-
-
Save n7best/6d72092e4628e75f3452 to your computer and use it in GitHub Desktop.
FineUploader Laravel Example with AWS SDK v3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWS_CLIENT_SECRET_KEY=FILLIN | |
AWS_SERVER_PUBLIC_KEY=FILLIN | |
AWS_SERVER_PRIVATE_KEY=FILLIN | |
S3_BUCKET_NAME=FILLIN | |
S3_HOST_NAME=FILLIN | |
S3_MAX_FILE_SIZE=5242880 FILLIN | |
S3_BUCKET_REGION=us-east-1 FILLIN | |
S3_BUCKET_VERSION=2006-03-01 FILLIN |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Http\Controllers; | |
use Illuminate\Http\Request; | |
use App\Http\Requests; | |
use App\Http\Controllers\Controller; | |
use Aws\S3\S3Client; | |
use Aws\Credentials\Credentials; | |
class S3UploadCtrl extends Controller | |
{ | |
private $clientPrivateKey; | |
private $serverPublicKey; | |
private $serverPrivateKey; | |
private $expectedBucketName; | |
private $expectedHostName; // v4-only | |
private $expectedMaxSize; | |
private $expectedBucketRegion; | |
private $expectedBucketVersion; | |
private $req; | |
public function __construct(Request $req){ | |
$this->clientPrivateKey = config('services.amazon.clientPrivateKey'); | |
$this->serverPublicKey = config('services.amazon.serverPublicKey'); | |
$this->serverPrivateKey = config('services.amazon.serverPrivateKey'); | |
$this->expectedBucketName = config('services.amazon.expectedBucketName'); | |
$this->expectedHostName = config('services.amazon.expectedHostName'); | |
$this->expectedMaxSize = config('services.amazon.expectedMaxSize'); | |
$this->expectedBucketRegion = config('services.amazon.expectedBucketRegion'); | |
$this->expectedBucketVersion = config('services.amazon.expectedBucketVersion'); | |
$this->req = $req; | |
} | |
public function endpoint(){ | |
if(isset($this->req->success)){ | |
return $this->verifyFileInS3($this->shouldIncludeThumbnail()); | |
}else{ | |
return $this->signRequest(); | |
} | |
} | |
private function getS3Client() { | |
$credentials = new Credentials($this->serverPublicKey,$this->serverPrivateKey); | |
return new S3Client([ | |
'region' => $this->expectedBucketRegion, | |
'version' => $this->expectedBucketVersion, | |
'credentials' => $credentials | |
]); | |
} | |
private function signRequest() { | |
$content = $this->req->getContent(); | |
$contentAsObject = json_decode($content, true); | |
$jsonContent = json_encode($contentAsObject); | |
if (isset($contentAsObject["headers"])) { | |
return $this->signRestRequest($contentAsObject["headers"]); | |
} | |
else { | |
return $this->signPolicy($jsonContent); | |
} | |
} | |
private function signPolicy($policyStr) { | |
$policyObj = json_decode($policyStr, true); | |
if ($this->isPolicyValid($policyObj)) { | |
$encodedPolicy = base64_encode($policyStr); | |
if ($this->req->has('v4')) { | |
$response = ['policy' => $encodedPolicy, 'signature' => $this->signV4Policy($encodedPolicy, $policyObj)]; | |
} | |
else { | |
$response = ['policy' => $encodedPolicy, 'signature' => $this->sign($encodedPolicy)]; | |
} | |
return response()->json($response); | |
} | |
else { | |
return response()->json(['invalid' => true]); | |
} | |
} | |
private function signV4Policy($stringToSign, $policyObj) { | |
foreach ($policyObj["conditions"] as $condition) { | |
if (isset($condition["x-amz-credential"])) { | |
$credentialCondition = $condition["x-amz-credential"]; | |
} | |
} | |
$pattern = "/.+\/(.+)\\/(.+)\/s3\/aws4_request/"; | |
preg_match($pattern, $credentialCondition, $matches); | |
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $this->clientPrivateKey, true); | |
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true); | |
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true); | |
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true); | |
return hash_hmac('sha256', $stringToSign, $signingKey); | |
} | |
private function isPolicyValid($policy) { | |
$conditions = $policy["conditions"]; | |
$bucket = null; | |
$parsedMaxSize = null; | |
for ($i = 0; $i < count($conditions); ++$i) { | |
$condition = $conditions[$i]; | |
if (isset($condition["bucket"])) { | |
$bucket = $condition["bucket"]; | |
} | |
else if (isset($condition[0]) && $condition[0] == "content-length-range") { | |
$parsedMaxSize = $condition[2]; | |
} | |
} | |
return $bucket == $this->expectedBucketName && $parsedMaxSize == (string)$this->expectedMaxSize; | |
} | |
private function signRestRequest($headersStr) { | |
$version = $this->req->has('v4') ? 4 : 2; | |
if ($this->isValidRestRequest($headersStr, $version)) { | |
if ($version == 4) { | |
$response = ['signature' => $this->signV4RestRequest($headersStr)]; | |
} | |
else { | |
$response = ['signature' => $this->sign($headersStr)]; | |
} | |
return response()->json($response); | |
} | |
else { | |
return response()->json(['invalid' => true]); | |
} | |
} | |
private function sign($stringToSign) { | |
return base64_encode(hash_hmac( | |
'sha1', | |
$stringToSign, | |
$this->clientPrivateKey, | |
true | |
)); | |
} | |
private function signV4RestRequest($rawStringToSign) { | |
$pattern = "/.+\\n.+\\n(\\d+)\/(.+)\/s3\/aws4_request\\n(.+)/s"; | |
preg_match($pattern, $rawStringToSign, $matches); | |
$hashedCanonicalRequest = hash('sha256', $matches[3]); | |
$stringToSign = preg_replace("/^(.+)\/s3\/aws4_request\\n.+$/s", '$1/s3/aws4_request'."\n".$hashedCanonicalRequest, $rawStringToSign); | |
$dateKey = hash_hmac('sha256', $matches[1], 'AWS4' . $this->clientPrivateKey, true); | |
$dateRegionKey = hash_hmac('sha256', $matches[2], $dateKey, true); | |
$dateRegionServiceKey = hash_hmac('sha256', 's3', $dateRegionKey, true); | |
$signingKey = hash_hmac('sha256', 'aws4_request', $dateRegionServiceKey, true); | |
return hash_hmac('sha256', $stringToSign, $signingKey); | |
} | |
private function isValidRestRequest($headersStr, $version) { | |
if ($version == 2) { | |
$expectedBucketName = $this->expectedBucketName; | |
$pattern = "/\/$expectedBucketName\/.+$/"; | |
} | |
else { | |
$expectedHostName = $this->expectedHostName; | |
$pattern = "/host:$expectedHostName/"; | |
} | |
preg_match($pattern, $headersStr, $matches); | |
return count($matches) > 0; | |
} | |
private function getObjectSize($bucket, $key) { | |
$objInfo = $this->getS3Client()->headObject([ | |
'Bucket' => $bucket, | |
'Key' => $key | |
]); | |
return $objInfo['ContentLength']; | |
} | |
// Provide a time-bombed public link to the file. | |
private function getTempLink($bucket, $key) { | |
$client = $this->getS3Client(); | |
$url = "{$bucket}/{$key}"; | |
$cmd = $client->getCommand('GetObject',[ | |
'Bucket' => $bucket, | |
'Key' => $key | |
]); | |
$request = $client->createPresignedRequest($cmd, '+15 minutes'); | |
return (string) $request->getUri(); | |
} | |
// Only needed if the delete file feature is enabled | |
private function deleteObject() { | |
$this->getS3Client()->deleteObject([ | |
'Bucket' => $this->req->bucket, | |
'Key' => $this->req->key | |
]); | |
} | |
// This is not needed if you don't require a callback on upload success. | |
private function verifyFileInS3($includeThumbnail) { | |
$bucket = $this->req->bucket; | |
$key = $this->req->key; | |
if (isset($this->expectedMaxSize) && $this->getObjectSize($bucket, $key) > $this->expectedMaxSize) { | |
$this->deleteObject(); | |
return response()->json(["error" => "File is too big!", "preventRetry" => true],500); | |
} | |
else { | |
$link = $this->getTempLink($bucket, $key); | |
$response =["tempLink" => $link]; | |
if ($includeThumbnail) { | |
$response["thumbnailUrl"] = $link; | |
} | |
return response()->json($response); | |
} | |
} | |
// Return true if it's likely that the associate file is natively | |
// viewable in a browser. For simplicity, just uses the file extension | |
// to make this determination, along with an array of extensions that one | |
// would expect all supported browsers are able to render natively. | |
protected function isFileViewableImage($filename) { | |
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); | |
$viewableExtensions = array("jpeg", "jpg", "gif", "png","svg"); | |
return in_array($ext, $viewableExtensions); | |
} | |
// Returns true if we should attempt to include a link | |
// to a thumbnail in the uploadSuccess response. In it's simplest form | |
// (which is our goal here - keep it simple) we only include a link to | |
// a viewable image and only if the browser is not capable of generating a client-side preview. | |
protected function shouldIncludeThumbnail() { | |
$filename = $this->req->name; | |
$isPreviewCapable = $this->req->isBrowserPreviewCapable == "true"; | |
$isFileViewableImage = $this->isFileViewableImage($filename); | |
return !$isPreviewCapable && $isFileViewableImage; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//config/services.php | |
//...all other services config | |
'amazon' => [ | |
'clientPrivateKey' => env('AWS_CLIENT_SECRET_KEY'), | |
'serverPublicKey' => env('AWS_SERVER_PUBLIC_KEY'), | |
'serverPrivateKey' => env('AWS_SERVER_PRIVATE_KEY'), | |
'expectedBucketName' => env('S3_BUCKET_NAME'), | |
'expectedHostName' => env('S3_HOST_NAME'), | |
'expectedMaxSize' => env('S3_MAX_FILE_SIZE',null), | |
'expectedBucketRegion' => env('S3_BUCKET_REGION','us-east-1'), | |
'expectedBucketVersion' => env('S3_BUCKET_VERSION','2006-03-01'), | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi,
Please upload this file also
Aws\S3\S3Client;
Aws\Credentials\Credentials;
you have not added.
i am getting issue when using deleteObject().