Last active
June 11, 2025 19:12
-
-
Save NazemMahmud/32b6278e421c545ca02119fa1915f8c3 to your computer and use it in GitHub Desktop.
downloading images from URLs and uploading them to your storage. It Includes proper error handling, validation, and cleanup.
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\Services; // this will be the namespace according to your file path | |
use Illuminate\Http\UploadedFile; | |
use Illuminate\Support\Facades\Http; | |
use Illuminate\Support\Facades\Log; | |
use Exception; | |
class ImageMigrationService | |
{ | |
private int $timeout = 30; // Set your own timeout value | |
public function downloadAndUploadImage(string $imageUrl, string $uploadPath = 'images'): array | |
{ | |
$tempPath = null; | |
try { | |
// Step 1: Download the image | |
$imageResponse = Http::timeout($this->timeout)->get($imageUrl); | |
if (!$imageResponse->successful()) { | |
throw new Exception("Failed to download image from URL: {$imageUrl}"); | |
} | |
$imageContent = $imageResponse->body(); | |
if (empty($imageContent)) { | |
throw new Exception("Downloaded image content is empty"); | |
} | |
// Step 2: Create temporary file | |
$originalFilename = $this->extractFilenameFromUrl($imageUrl); | |
$tempPath = rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . uniqid('img_') . '_' . $originalFilename; | |
if (file_put_contents($tempPath, $imageContent) === false) { | |
throw new Exception("Failed to write image to temporary file"); | |
} | |
// Step 3: Validate the file is actually an image | |
$mimeType = mime_content_type($tempPath); | |
if (!$mimeType || !str_starts_with($mimeType, 'image/')) { | |
throw new Exception('Downloaded file is not a valid image (MIME: ' . $mimeType . ')'); | |
} | |
// Step 4: Create UploadedFile instance | |
$uploadedFile = new UploadedFile( | |
$tempPath, | |
$originalFilename, | |
$mimeType, | |
null, | |
true | |
); | |
// Step 5: Upload to your storage (customize this part according to your project requirement) | |
$uploadResult = $this->uploadToStorage($uploadedFile, $uploadPath); | |
return $uploadResult; | |
} catch (Exception $ex) { | |
if ($tempPath && file_exists($tempPath)) { | |
unlink($tempPath); | |
} | |
throw $ex; | |
} | |
} | |
/** | |
* this is a sample example, your upload code might be different based on your requirements | |
* such as, you might want to upload in aws s3 storage, so upload method would be different for you | |
*/ | |
private function uploadToStorage(UploadedFile $file, string $path): array | |
{ | |
try { | |
// Example using Laravel's storage facade | |
$filename = time() . '_' . $file->getClientOriginalName(); | |
$storedPath = $file->storeAs($path, $filename, 'public'); | |
return [ | |
'success' => true, | |
'path' => $storedPath, | |
'url' => asset('storage/' . $storedPath), | |
'filename' => $filename | |
]; | |
} finally { | |
// Step 6: Always clean up temporary file, | |
// don't use finally inside download method above | |
// because, at that time temporary file will get deleted before file upload and you will get error | |
if ($tempPath && file_exists($tempPath)) { | |
unlink($tempPath); | |
} | |
} | |
} | |
private function extractFilenameFromUrl(string $url): string | |
{ | |
$filename = basename(parse_url($url, PHP_URL_PATH)); | |
if (empty($filename) || !pathinfo($filename, PATHINFO_EXTENSION)) { | |
$filename = 'image_' . time() . '_' . uniqid() . '.jpg'; | |
} | |
// Sanitize filename | |
return preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment