Running your Laravel project on AWS Lambda

Running your Laravel project on AWS Lambda

Ever find yourself deploying your Laravel app to your EC2 boxes and wondering if you’re using your resources wisely? You’re almost certainly paying for CPU time you’re not using, and scaling PHP applications on AWS can be tricky.

Wouldn’t it be great to pay only for the resources you actually use, and if your site could scale instantly without needing to configure auto-scaling-groups, alarms, and rules? AWS Lambda gives us these features but doesn't natively support PHP.

Until now making PHP run on AWS Lambda has required a lot of hacks and workarounds. However, evolving tools such as https://bref.sh have fixed a lot of the pain for you.

In this session we’ll walk through the steps required to set up a real Laravel website running on Lambda, served over HTTPS, and all without launching a single EC2 instance or ELB.

Finally we'll look at some of the other possibilities and considerations that are now open to us as PHP engineers in a serverless world.

Da2d2829b89cde136392973a35b68959?s=128

nealio82

June 11, 2019
Tweet

Transcript

  1. 2.
  2. 3.
  3. 4.
  4. 6.
  5. 9.
  6. 10.
  7. 11.
  8. 12.
  9. 13.

    EC2

  10. 17.
  11. 26.
  12. 29.
  13. 30.
  14. 36.
  15. 37.
  16. 38.
  17. 58.
  18. 59.
  19. 60.
  20. 66.
  21. 67.
  22. 71.

    What kind of lambda do you want to create? [0]

    PHP function [1] HTTP application [2] Console application >
  23. 72.

    What kind of lambda do you want to create? [0]

    PHP function [1] HTTP application [2] Console application > 1
  24. 74.
  25. 75.

    [OK] Project initialized and ready to test or deploy. The

    files created were automatically added to git.
  26. 78.

    AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: '' Resources: MyFunction: Type: AWS::Serverless::Function

    Properties: FunctionName: 'my-function' Description: '' CodeUri: . Handler: index.php Timeout: 30 # in seconds (API Gateway has a timeout of 30 se Runtime: provided Layers: - ‘arn:aws:lambda:eu-west-1:209497400698:layer:php-73-fp Events: # The function will match all HTTP URLs HttpRoot: Type: Api Properties: Path: / Method: ANY HttpSubPaths: Type: Api Properties: Path: /{proxy+} hello-world.php template.yaml
  27. 79.

    # ... Resources: MyFunction: Type: AWS::Serverless::Function Properties: FunctionName: 'my-function' Description:

    '' CodeUri: . Handler: index.php Timeout: 30 # in seconds Runtime: provided hello-world.php template.yaml
  28. 80.

    # ... Resources: MyFunction: Type: AWS::Serverless::Function Properties: FunctionName: 'bref-hello-world' Description:

    '' CodeUri: . Handler: hello-world.php Timeout: 30 # in seconds Runtime: provided hello-world.php template.yaml
  29. 81.

    # ... Layers: - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73-fpm:1' Events: # The function will

    match all HTTP URLs HttpRoot: Type: Api Properties: Path: / Method: ANY HttpSubPaths: Type: Api Properties: Path: /{proxy+} Method: ANY # ... hello-world.php template.yaml
  30. 82.

    # ... Layers: - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73-fpm:1' Events: # The function will

    match all HTTP URLs HttpRoot: Type: Api Properties: Path: / Method: ANY HttpSubPaths: Type: Api Properties: Path: /{proxy+} Method: ANY # ... hello-world.php template.yaml
  31. 83.

    # ... Layers: - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73-fpm:1' Events: # The function will

    match all HTTP URLs HttpRoot: Type: Api Properties: Path: / Method: ANY HttpSubPaths: Type: Api Properties: Path: /{proxy+} Method: ANY # ... hello-world.php template.yaml
  32. 84.

    # ... Layers: - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73-fpm:1' Events: # The function will

    match all HTTP URLs HttpRoot: Type: Api Properties: Path: / Method: ANY HttpSubPaths: Type: Api Properties: Path: /{proxy+} Method: ANY # ... hello-world.php template.yaml
  33. 85.
  34. 86.
  35. 87.
  36. 93.
  37. 94.

    Execute the following command to deploy the packaged template aws

    cloudformation deploy --template-file / Users/neal/www/hello-world/.stack.yaml --stack- name <YOUR STACK NAME>
  38. 100.

    Failed to create/update the stack. Run the following command to

    fetch the list of events leading up to the failure aws cloudformation describe-stack-events -- stack-name bref-hello-world
  39. 102.
  40. 103.
  41. 104.
  42. 109.
  43. 110.
  44. 112.
  45. 113.
  46. 114.
  47. 115.
  48. 116.
  49. 127.
  50. 128.
  51. 129.
  52. 130.
  53. 131.
  54. 137.

    What kind of lambda do you want to create? [0]

    PHP function [1] HTTP application [2] Console application > 1
  55. 138.
  56. 139.

    # ... Resources: WebApplication: Type: AWS::Serverless::Function Properties: FunctionName: ‘bref-kittyfinder' Description:

    '' CodeUri: . Handler: public/index.php Timeout: 30 # in seconds Runtime: provided MemorySize: 1024 app config database bootstrap template.yaml public resources routes storage
  57. 140.

    # ... Resources: WebApplication: Type: AWS::Serverless::Function Properties: FunctionName: ‘bref-kittyfinder' Description:

    '' CodeUri: . Handler: public/index.php Timeout: 30 # in seconds Runtime: provided MemorySize: 1024 app config database bootstrap template.yaml public resources routes storage
  58. 141.

    # ... Resources: WebApplication: Type: AWS::Serverless::Function Properties: FunctionName: ‘bref-kittyfinder' Description:

    '' CodeUri: . Handler: public/index.php Timeout: 30 # in seconds Runtime: provided MemorySize: 1024 app config database bootstrap template.yaml public resources routes storage
  59. 145.

    Failed to create/update the stack. Run the following command to

    fetch the list of events leading up to the failure aws cloudformation describe-stack-events -- stack-name bref-kitty-finder
  60. 149.

    cp -Rf ../bref-kitty-finder/* . \ cp ../bref-kitty-finder/.env . \ &&

    rm -rf node_modules \ && composer install --optimize-autoloader --no-dev \ && sam package --output-template-file .stack.yaml --s3-bucket bref-kitty-finder-bucket \ && sam deploy --template-file .stack.yaml --stack-name bref-kitty-finder --capabilities CAPABILITY_IAM
  61. 151.
  62. 154.

    "NOTICE: PHP message: PHP Fatal error: Uncaught ErrorException: file_put_contents(/var/task/ storage/framework/views/

    a048d5da056b113da279d23ae842d71b174d2ae6.php): failed to open stream: Read-only file system in /var/task/vendor/laravel/framework/src/ Illuminate/Filesystem/Filesystem.php:122"
  63. 155.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' Resources: WebApplication: # ... app config database bootstrap template.yaml public resources routes storage
  64. 156.

    <?php /* |----------------------------------------------------------------------- | Create The Application |----------------------------------------------------------------------- | |

    The first thing we will do is create a new Laravel application instanc | which serves as the "glue" for all the components of Laravel, and is | the IoC container for the system binding all of the various parts. | */ $app = new Illuminate\Foundation\Application( $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) ); /* * Allow overriding the storage path in * production using an environment variable. */ $app->useStoragePath($_ENV['APP_STORAGE'] ?? $app->storagePath()); app config database bootstrap app.php public resources routes storage
  65. 157.

    <?php /* |----------------------------------------------------------------------- | Create The Application |----------------------------------------------------------------------- | |

    The first thing we will do is create a new Laravel application instanc | which serves as the "glue" for all the components of Laravel, and is | the IoC container for the system binding all of the various parts. | */ $app = new Illuminate\Foundation\Application( $_ENV['APP_BASE_PATH'] ?? dirname(__DIR__) ); /* * Allow overriding the storage path in * production using an environment variable. */ $app->useStoragePath($_ENV['APP_STORAGE'] ?? $app->storagePath()); app config database bootstrap public resources routes storage app.php
  66. 158.

    # ... VIEW_COMPILED_PATH=/tmp/storage/framework/views # We cannot store sessions to disk:

    if you don't need sessions (e.g. API # then use `array`, else store sessions in database or cookies SESSION_DRIVER=array # Logging to stderr allows the logs to end up in Cloudwatch LOG_CHANNEL=stderr # ... app config database bootstrap .env public resources routes storage
  67. 159.

    # ... VIEW_COMPILED_PATH=/tmp/storage/framework/views # We cannot store sessions to disk:

    if you don't need sessions (e.g. API # then use `array`, else store sessions in database or cookies SESSION_DRIVER=array # Logging to stderr allows the logs to end up in Cloudwatch LOG_CHANNEL=stderr # ... app config database bootstrap .env public resources routes storage
  68. 160.

    # ... VIEW_COMPILED_PATH=/tmp/storage/framework/views # We cannot store sessions to disk:

    if you don't need sessions (e.g. API # then use `array`, else store sessions in database or cookies SESSION_DRIVER=array # Logging to stderr allows the logs to end up in Cloudwatch LOG_CHANNEL=stderr # ... app config database bootstrap .env public resources routes storage
  69. 161.

    # ... VIEW_COMPILED_PATH=/tmp/storage/framework/views # We cannot store sessions to disk:

    if you don't need sessions (e.g. API # then use `array`, else store sessions in database or cookies SESSION_DRIVER=array # Logging to stderr allows the logs to end up in Cloudwatch LOG_CHANNEL=stderr # ... app config database bootstrap .env public resources routes storage
  70. 162.

    <?php class AppServiceProvider extends ServiceProvider { // ... /** *

    Bootstrap any application services. * * @return void */ public function boot() { } } app config database Providers bootstrap AppServiceProvider.php public resources routes storage
  71. 163.

    <?php class AppServiceProvider extends ServiceProvider { // ... /** *

    Bootstrap any application services. * * @return void */ public function boot() { // Make sure the directory for compiled views exist if (!is_dir(config('view.compiled'))) { mkdir(config('view.compiled'), 0755, true); } } } app config database Providers public resources routes storage bootstrap AppServiceProvider.php
  72. 164.
  73. 165.
  74. 166.
  75. 173.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘mysql://db_user:db_pass@...’ # ... app config database bootstrap template.yaml public resources routes storage
  76. 174.
  77. 175.
  78. 176.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘{{resolve:ssm:kittyfinder-database-url:1}}' # ... app config database bootstrap template.yaml public resources routes storage
  79. 177.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘{{resolve:ssm:kittyfinder-database-url:1}}' # ... app config database bootstrap template.yaml public resources routes storage
  80. 178.
  81. 183.

    app config database bootstrap filesystems.php <?php return [ 'disks' =>

    [ // ... /* |----------------------------------------------------------------- | Assets CDN |----------------------------------------------------------------- | | This is where compiled static assets will be served from */ 'asset-cdn' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'bucket' => env(‘AWS_CDN_BUCKET'), ], ], ]; public resources routes storage
  82. 184.

    app config database bootstrap filesystems.php <?php return [ 'disks' =>

    [ // ... /* |----------------------------------------------------------------- | Assets CDN |----------------------------------------------------------------- | | This is where compiled static assets will be served from */ 'asset-cdn' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 'bucket' => env(‘AWS_CDN_BUCKET'), ], ], ]; public resources routes storage
  83. 185.

    <?php return [ 'use_cdn' => env('USE_CDN', false), 'cdn_url' => 'https://cdn.kittyfinder.net',

    'filesystem' => [ 'disk' => 'asset-cdn', 'options' => [ 'ACL' => 'public-read', 'CacheControl' => 'max-age=31536000, public’, // ... ], 'files' => [ 'include' => [ 'paths' => [ 'js', 'css' ], // ... app config database bootstrap asset-cdn.php public resources routes storage
  84. 186.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘{{resolve:ssm:kittyfinder-database-url:1}}' USE_CDN: true # ... app config database bootstrap template.yaml public resources routes storage
  85. 187.

    <!-- Scripts --> <script src="{{ mix('js/app.js') }}" defer></script> <!-- Fonts

    --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet <!-- Styles --> <link href="{{ mix('css/app.css') }}" rel="stylesheet"> app config database bootstrap public resources routes storage views layouts app.blade.php
  86. 188.

    <!-- Scripts --> <script src="{{ mix_cdn('js/app.js') }}" defer></script> <!-- Fonts

    --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet <!-- Styles --> <link href="{{ mix_cdn('css/app.css') }}" rel="stylesheet"> app config database bootstrap public resources routes storage views layouts app.blade.php
  87. 189.
  88. 191.
  89. 194.
  90. 195.
  91. 196.
  92. 197.
  93. 198.
  94. 199.
  95. 200.
  96. 201.

    <?php return [ 'use_cdn' => env('USE_CDN', false), 'cdn_url' => 'https://cdn.kittyfinder.net',

    'filesystem' => [ 'disk' => 'asset-cdn', 'options' => [ 'ACL' => 'public-read', 'CacheControl' => 'max-age=31536000, public’, // ... ], 'files' => [ 'include' => [ 'paths' => [ 'js', 'css', 'vendor' ], // ... app config database bootstrap asset-cdn.php public resources routes storage
  97. 202.

    <!-- Styles --> <link rel="stylesheet" href="{{ mix_cdn('app.css', 'vendor/nova') }}"> app

    config database bootstrap public resources routes storage views vendor nova layout.blade.php
  98. 203.
  99. 204.
  100. 205.

    <?php class AppServiceProvider extends ServiceProvider { // ... /** *

    Bootstrap any application services. * * @return void */ public function boot() { // Make sure the directory for compiled views exist if (!is_dir(config('view.compiled'))) { mkdir(config('view.compiled'), 0755, true); } if($this->app->environment('production')) { URL::forceScheme('https'); } } } app config database Providers public resources routes storage bootstrap AppServiceProvider.php
  101. 206.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘{{resolve:ssm:kittyfinder-database-url:1}}' USE_CDN: true APP_ENV: production # ... app config database bootstrap template.yaml public resources routes storage
  102. 207.
  103. 211.
  104. 212.

    API Gateway Data Store Sessions Sessions Handler PHP Layer Bootstrap

    Lambda function Handler PHP Layer Bootstrap
  105. 214.

    # ... Globals: Function: Environment: Variables: # Laravel environment variables

    APP_STORAGE: '/tmp' DATABASE_URL: ‘{{resolve:ssm:kittyfinder-database-url:1}}' USE_CDN: true APP_ENV: production SESSION_DRIVER: database # ... app config database bootstrap template.yaml public resources routes storage
  106. 217.

    Resources: WebApplication: # ... Console: Type: AWS::Serverless::Function Properties: FunctionName: 'bref-kittyfinder-console'

    CodeUri: . Handler: artisan Runtime: provided Layers: # PHP runtime - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73:1' # Console layer - 'arn:aws:lambda:eu-west-1:209497400698:layer:console:1' app config database bootstrap template.yaml public resources routes storage
  107. 218.

    app config database bootstrap Resources: WebApplication: # ... Console: Type:

    AWS::Serverless::Function Properties: FunctionName: 'bref-kittyfinder-console' CodeUri: . Handler: artisan Runtime: provided Layers: # PHP runtime - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73:1' # Console layer - 'arn:aws:lambda:eu-west-1:209497400698:layer:console:1' template.yaml public resources routes storage
  108. 219.

    app config database bootstrap template.yaml public resources routes storage Resources:

    WebApplication: # ... Console: Type: AWS::Serverless::Function Properties: FunctionName: 'bref-kittyfinder-console' CodeUri: . Handler: artisan Runtime: provided Layers: # PHP runtime - 'arn:aws:lambda:eu-west-1:209497400698:layer:php-73:1' # Console layer - 'arn:aws:lambda:eu-west-1:209497400698:layer:console:1'
  109. 224.
  110. 229.

    /** * Get the fields displayed by the resource. *

    * @param \Illuminate\Http\Request $request * @return array */ public function fields(Request $request) { return [ // ... Avatar::make('Avatar', 'profile_pic')->disk('s3'), // ... ]; } app config database bootstrap Nova Kitty.php User.php public resources routes storage
  111. 230.

    app config database bootstrap Nova Kitty.php User.php /** * Get

    the fields displayed by the resource. * * @param \Illuminate\Http\Request $request * @return array */ public function fields(Request $request) { return [ // ... Avatar::make('Avatar')->disk('s3'), // ... ]; } public resources routes storage
  112. 231.
  113. 232.
  114. 235.
  115. 242.
  116. 243.

    Client Server Amazon S3 Get Pre-Signed URL Get Pre-Signed URL

    One-time use URL One-time use URL PUT file to One-time use URL 200 OK
  117. 244.

    Client Server Amazon S3 Get Pre-Signed URL Get Pre-Signed URL

    One-time use URL One-time use URL PUT file to One-time use URL 200 OK Update record
  118. 245.

    Client Server Amazon S3 Get Pre-Signed URL Get Pre-Signed URL

    One-time use URL One-time use URL PUT file to One-time use URL 200 OK Update record Delete previous file
  119. 247.

    /** * Get the fields displayed by the resource. *

    * @param \Illuminate\Http\Request $request * @return array */ public function fields(Request $request) { return [ ID::make()->sortable(), Text::make('Name') ->sortable() ->rules('required', 'max:255'), Avatar::make(‘Avatar', 'profile_pic'), Text::make('Favourite Toy'), Trix::make('Bio'), BelongsTo::make('User') ]; } app config database bootstrap public resources routes storage Nova Kitty.php User.php nova-components
  120. 248.

    /** * Get the fields displayed by the resource. *

    * @param \Illuminate\Http\Request $request * @return array */ public function fields(Request $request) { return [ ID::make()->sortable(), Text::make('Name') ->sortable() ->rules('required', 'max:255'), S3FilePicker::make('Image', 'profile_pic'), Text::make('Favourite Toy'), Trix::make('Bio'), BelongsTo::make('User') ]; } app config database bootstrap public resources routes storage Nova Kitty.php User.php nova-components
  121. 249.

    class ApiController extends Controller { /** * Get a pre-signed

    key * * @return Response */ public function preSignedUrl(Request $request): Response { if (null === $request->user()) { throw new UnauthorizedHttpException("You're not allowed her } return response()->json([ 'url' => $this->generatePreSignedUrl($request), 'filename' => $this->getNewFilename($request), 'path' => 'https://' . env('AWS_BUCKET') . '.s3-eu-west-1.amazonaws.com/' . $this->getNewFilename($request) ]); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  122. 250.

    class ApiController extends Controller { /** * Get a pre-signed

    key * * @return Response */ public function preSignedUrl(Request $request): Response { if (null === $request->user()) { throw new UnauthorizedHttpException("You're not allowed her } return response()->json([ 'url' => $this->generatePreSignedUrl($request), 'filename' => $this->getNewFilename($request), 'path' => 'https://' . env('AWS_BUCKET') . '.s3-eu-west-1.amazonaws.com/' . $this->getNewFilename($request) ]); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  123. 251.

    class ApiController extends Controller { // ... private function generatePreSignedUrl(Request

    $request): string { $s3Client = new S3Client([ 'region' => env('AWS_DEFAULT_REGION'), 'version' => 'latest', ]); $cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => env('AWS_BUCKET'), 'Key' => $this->getNewFilename($request), 'ContentType' => $request->get('filetype') ]); $response = $s3Client->createPresignedRequest( $cmd, '+20 minutes’ ); return (string)$response->getUri(); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  124. 252.

    class ApiController extends Controller { // ... private function generatePreSignedUrl(Request

    $request): string { $s3Client = new S3Client([ 'region' => env('AWS_DEFAULT_REGION'), 'version' => 'latest', ]); $cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => env('AWS_BUCKET'), 'Key' => $this->getNewFilename($request), 'ContentType' => $request->get('filetype') ]); $response = $s3Client->createPresignedRequest( $cmd, '+20 minutes’ ); return (string)$response->getUri(); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  125. 253.

    class ApiController extends Controller { // ... private function generatePreSignedUrl(Request

    $request): string { $s3Client = new S3Client([ 'region' => env('AWS_DEFAULT_REGION'), 'version' => 'latest', ]); $cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => env('AWS_BUCKET'), 'Key' => $this->getNewFilename($request), 'ContentType' => $request->get('filetype') ]); $response = $s3Client->createPresignedRequest( $cmd, '+20 minutes’ ); return (string)$response->getUri(); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  126. 254.

    class ApiController extends Controller { // ... private function generatePreSignedUrl(Request

    $request): string { $s3Client = new S3Client([ 'region' => env('AWS_DEFAULT_REGION'), 'version' => 'latest', ]); $cmd = $s3Client->getCommand('PutObject', [ 'Bucket' => env('AWS_BUCKET'), 'Key' => $this->getNewFilename($request), 'ContentType' => $request->get('filetype') ]); $response = $s3Client->createPresignedRequest( $cmd, '+20 minutes’ ); return (string)$response->getUri(); } // ... app config database bootstrap public resources routes storage Http ApiController.php Controllers nova-components
  127. 255.

    <template> <default-field :field="field" :errors="errors"> // ... <template slot="field"> <input ref="fileField"

    type="file" name="imageUpload" @change="fileChange" /> <input type="text" :id="field.name" :dusk="field.attribute" name="field.name" v-model="value" /> </template> </default-field> </template> app config database nova-components bootstrap public resources routes storage resources/js/components S3FilePicker FormField.vue
  128. 256.

    app config database nova-components bootstrap public resources routes storage resources/js/components

    S3FilePicker FormField.vue /** * Update the field's internal value. */ async fileChange(event) { let path = event.target.value let fileName = path.match(/[^\\/]*$/)[0] this.fileName = fileName this.file = this.$refs.fileField.files[0] await axios.get('/nova/presignedurl?' + 'filename=' + this.fileName + '&filetype=' + this.file.type ).then(async response => { let options = { headers: { "Content-Type": this.file.type } }; await axios.put(response.data.url, this.file, options).then(result document.getElementById(‘previewImage') .setAttribute(‘src', response.data.path); this.value = response.data.filename }); }) }
  129. 257.

    app config database nova-components bootstrap public resources routes storage resources/js/components

    S3FilePicker FormField.vue /** * Update the field's internal value. */ async fileChange(event) { let path = event.target.value let fileName = path.match(/[^\\/]*$/)[0] this.fileName = fileName this.file = this.$refs.fileField.files[0] await axios.get('/nova/presignedurl?' + 'filename=' + this.fileName + '&filetype=' + this.file.type ).then(async response => { let options = { headers: { "Content-Type": this.file.type } }; await axios.put(response.data.url, this.file, options).then(result document.getElementById(‘previewImage') .setAttribute(‘src', response.data.path); this.value = response.data.filename }); }) }
  130. 258.

    app config database nova-components bootstrap public resources routes storage resources/js/components

    S3FilePicker FormField.vue /** * Update the field's internal value. */ async fileChange(event) { let path = event.target.value let fileName = path.match(/[^\\/]*$/)[0] this.fileName = fileName this.file = this.$refs.fileField.files[0] await axios.get('/nova/presignedurl?' + 'filename=' + this.fileName + '&filetype=' + this.file.type ).then(async response => { let options = { headers: { "Content-Type": this.file.type } }; await axios.put(response.data.url, this.file, options).then(result document.getElementById(‘previewImage') .setAttribute(‘src', response.data.path); this.value = response.data.filename }); }) }
  131. 259.
  132. 260.
  133. 261.
  134. 262.
  135. 263.
  136. 264.
  137. 265.
  138. 267.
  139. 268.
  140. 274.
  141. 276.
  142. 281.
  143. 282.
  144. 283.
  145. 295.

    What kind of lambda do you want to create? [0]

    PHP function [1] HTTP application [2] Console application >
  146. 296.

    What kind of lambda do you want to create? [0]

    PHP function [1] HTTP application [2] Console application > 0
  147. 300.
  148. 302.
  149. 303.