Skip to content

Instantly share code, notes, and snippets.

@ajaxray
Created July 2, 2024 09:55
Show Gist options
  • Save ajaxray/5d8cd57aba4e39cc56924318ce869b8c to your computer and use it in GitHub Desktop.
Save ajaxray/5d8cd57aba4e39cc56924318ce869b8c to your computer and use it in GitHub Desktop.
Upload to S3 compliant storage directly from Frontend

Upload files to object storage / CDN directly from the Frontend

This is a demonstration of uploading files to S3 compliant storage. This implementation is using Laravel (backend) and AlpineJS (frontend).

How it works

  1. User selects a file on frontend
  2. Javascript makes a request to the backend for getting a TemporaryUploadUrl.
  3. On receiving the signed, temporary URL, Javascript pushes the file to S3 directly from front-end.
  4. After the upload is done, we have the CDB URL of the file (to submit and store in backend).
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Direct CDN Upload') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div x-data="fileUpload()" class="p-6">
<form @submit.prevent="uploadFile">
<input type="file" x-ref="fileInput">
<button class="px-4 py-2 bg-blue-500 text-white rounded" type="submit">Upload</button>
</form>
<template x-if="isUploading">
<p class="p-4 text-orange-400">Uploading...</p>
</template>
<template x-if="uploadSuccess">
<p class="p4 text-green-600">Upload successful! File path: <span x-text="filePath"></span></p>
</template>
</div>
</div>
</div>
</div>
<script>
function fileUpload() {
return {
isUploading: false,
uploadSuccess: false,
filePath: '',
async uploadFile() {
this.isUploading = true;
this.uploadSuccess = false;
const file = this.$refs.fileInput.files[0];
if (!file) {
alert('Please select a file.');
this.isUploading = false;
return;
}
try {
// Step 1: Get temporary upload URL
const response = await fetch('/upload/s3-get-upload-url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
},
body: JSON.stringify({
fileName: file.name
})
});
const data = await response.json();
const uploadUrl = data.url;
this.filePath = data.filePath;
// Step 2: Upload the file to S3 using the temporary URL
const uploadResponse = await fetch(uploadUrl, {
method: 'PUT',
headers: {
'Content-Type': file.type
},
body: file
});
if (uploadResponse.ok) {
this.uploadSuccess = true;
} else {
alert('Failed to upload file.');
}
} catch (error) {
console.error('Error uploading file:', error);
alert('An error occurred while uploading the file.');
} finally {
this.isUploading = false;
}
}
};
}
</script>
</x-app-layout>
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class S3DirectUploadController extends Controller
{
public function getTemporaryUploadUrl(Request $request)
{
$validatedData = $request->validate([
'fileName' => 'required|string',
]);
$fileName = $validatedData['fileName'];
$path = 'direct_uploads/' . uniqid() . '-' . $fileName;
['url' => $url, 'headers' => $headers] = Storage::disk('s3')->temporaryUploadUrl(
$path, now()->addMinutes(5)
);
return response()->json(['url' => $url, 'filePath' => $path]);
}
}
<?php
use App\Http\Controllers\S3DirectUploadController;
use Illuminate\Support\Facades\Route;
// Other Routes ...
Route::middleware('auth')->group(function () {
// Other Routes ...
Route::view('/upload/s3-direct', 'upload.s3_direct');
Route::post('/upload/s3-get-upload-url', [S3DirectUploadController::class, 'getTemporaryUploadUrl']);
});
require __DIR__.'/auth.php';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment