Download-Button ergänzt

This commit is contained in:
2025-11-12 11:20:12 +01:00
parent 0c83b15636
commit 33d374304e
6 changed files with 123 additions and 1 deletions

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class DownloadController extends Controller
{
public function downloadImage(Request $request)
{
$request->validate([
'image_path' => 'required|string',
]);
$imagePath = $request->input('image_path');
// Check if it's a relative path and make it absolute
if (!filter_var($imagePath, FILTER_VALIDATE_URL)) {
$imagePath = public_path(str_replace(url('/'), '', $imagePath));
}
// Validate file exists
if (!file_exists($imagePath)) {
Log::error("DownloadController: Image file not found at {$imagePath}");
return response()->json(['error' => 'Image file not found.'], 404);
}
// Get file info
$fileName = basename($imagePath);
$fileExtension = pathinfo($fileName, PATHINFO_EXTENSION);
$mimeType = $this->getMimeType($fileExtension);
try {
Log::info("DownloadController: Serving download for {$imagePath}");
// Return the file with proper headers for download
return response()->download($imagePath, $fileName, [
'Content-Type' => $mimeType,
'Content-Disposition' => 'attachment; filename="' . $fileName . '"',
]);
} catch (\Exception $e) {
Log::error("DownloadController: Error serving download: " . $e->getMessage());
return response()->json(['error' => 'Failed to serve download.'], 500);
}
}
private function getMimeType(string $extension): string
{
$mimeTypes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
'bmp' => 'image/bmp',
'svg' => 'image/svg+xml',
];
return $mimeTypes[strtolower($extension)] ?? 'application/octet-stream';
}
}

View File

@@ -21,6 +21,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" class="w-8 h-8 inline-block align-middle mr-2"><!--!Font Awesome Pro v6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc.--><path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM183.2 132.6c-1.3-2.8-4.1-4.6-7.2-4.6s-5.9 1.8-7.2 4.6l-16.6 34.7-38.1 5c-3.1 .4-5.6 2.5-6.6 5.5s-.1 6.2 2.1 8.3l27.9 26.5-7 37.8c-.6 3 .7 6.1 3.2 7.9s5.8 2 8.5 .6L176 240.5l33.8 18.3c2.7 1.5 6 1.3 8.5-.6s3.7-4.9 3.2-7.9l-7-37.8L242.4 186c2.2-2.1 3.1-5.3 2.1-8.3s-3.5-5.1-6.6-5.5l-38.1-5-16.6-34.7zm160 0c-1.3-2.8-4.1-4.6-7.2-4.6s-5.9 1.8-7.2 4.6l-16.6 34.7-38.1 5c-3.1 .4-5.6 2.5-6.6 5.5s-.1 6.2 2.1 8.3l27.9 26.5-7 37.8c-.6 3 .7 6.1 3.2 7.9s5.8 2 8.5 .6L336 240.5l33.8 18.3c2.7 1.5 6 1.3 8.5-.6s3.7-4.9 3.2-7.9l-7-37.8L402.4 186c2.2-2.1 3.1-5.3 2.1-8.3s-3.5-5.1-6.6-5.5l-38.1-5-16.6-34.7zm6.3 175.8c-28.9 6.8-60.5 10.5-93.6 10.5s-64.7-3.7-93.6-10.5c-18.7-4.4-35.9 12-25.5 28.1c24.6 38.1 68.7 63.5 119.1 63.5s94.5-25.4 119.1-63.5c10.4-16.1-6.8-32.5-25.5-28.1z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" class="w-8 h-8 inline-block align-middle mr-2"><!--!Font Awesome Pro v6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2025 Fonticons, Inc.--><path d="M256 48a208 208 0 1 1 0 416 208 208 0 1 1 0-416zm0 464A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM183.2 132.6c-1.3-2.8-4.1-4.6-7.2-4.6s-5.9 1.8-7.2 4.6l-16.6 34.7-38.1 5c-3.1 .4-5.6 2.5-6.6 5.5s-.1 6.2 2.1 8.3l27.9 26.5-7 37.8c-.6 3 .7 6.1 3.2 7.9s5.8 2 8.5 .6L176 240.5l33.8 18.3c2.7 1.5 6 1.3 8.5-.6s3.7-4.9 3.2-7.9l-7-37.8L242.4 186c2.2-2.1 3.1-5.3 2.1-8.3s-3.5-5.1-6.6-5.5l-38.1-5-16.6-34.7zm160 0c-1.3-2.8-4.1-4.6-7.2-4.6s-5.9 1.8-7.2 4.6l-16.6 34.7-38.1 5c-3.1 .4-5.6 2.5-6.6 5.5s-.1 6.2 2.1 8.3l27.9 26.5-7 37.8c-.6 3 .7 6.1 3.2 7.9s5.8 2 8.5 .6L336 240.5l33.8 18.3c2.7 1.5 6 1.3 8.5-.6s3.7-4.9 3.2-7.9l-7-37.8L402.4 186c2.2-2.1 3.1-5.3 2.1-8.3s-3.5-5.1-6.6-5.5l-38.1-5-16.6-34.7zm6.3 175.8c-28.9 6.8-60.5 10.5-93.6 10.5s-64.7-3.7-93.6-10.5c-18.7-4.4-35.9 12-25.5 28.1c24.6 38.1 68.7 63.5 119.1 63.5s94.5-25.4 119.1-63.5c10.4-16.1-6.8-32.5-25.5-28.1z"/></svg>
Stil ändern Stil ändern
</button> </button>
<button v-if="isNonLocalAccess()" @click="$emit('download', image)" class="context-menu-button">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" class="w-8 h-8 inline-block align-middle mr-2"><!--!Font Awesome Free v6.7.2 by @fontawesome - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V240c0 8.8-7.2 16-16 16s-16-7.2-16-16V64.7c0-5.4-1.2-10.7-3.2-15.7l-8.4-8.4c-2.3-2.3-2.3-6.1 0-8.5s6.1-2.3 8.5 0l8.4 8.4c5.3 5.3 11.4 8.6 18.2 9.7L338.6 59.2c11.4 1.9 23.5 2.9 35.4 2.9s24-1 35.4-2.9l48.6-9.1c6.8-1.1 12.9-4.4 18.2-9.7l8.4-8.4c2.3-2.3 6.1-2.3 8.5 0s2.3 6.1 0 8.5l-8.4 8.4c-2 5-3.2 10.3-3.2 15.7V240c0 8.8-7.2 16-16 16s-16-7.2-16-16V32zm-32 264.3c-43.7-5.5-79.7-30.9-90.7-69.7H128c-5.4 0-9.8-4.4-9.8-9.8s4.4-9.8 9.8-9.8h128c8.8 0 16 7.2 16 16s-7.2 16-16 16H133.3c18.1 30.9 55.4 56.9 100.7 56.9s82.6-26 100.7-56.9H384c8.8 0 16 7.2 16 16s-7.2 16-16 16H128c-5.4 0-9.8-4.4-9.8-9.8s4.4-9.8 9.8-9.8h136.3c-11 38.8-47 64.2-90.7 69.7z"/></svg>
Herunterladen
</button>
</div> </div>
<StyleSelector <StyleSelector
v-else v-else
@@ -53,7 +57,16 @@ const props = defineProps({
}, },
}); });
const emits = defineEmits(['close', 'print', 'changeStyle', 'styleSelected']); const emits = defineEmits(['close', 'print', 'changeStyle', 'styleSelected', 'download']);
const isNonLocalAccess = () => {
const hostname = window.location.hostname;
return hostname !== 'localhost' &&
hostname !== '127.0.0.1' &&
!hostname.startsWith('192.168.') &&
!hostname.startsWith('10.') &&
!hostname.startsWith('172.');
};
const showStyleSelectorView = ref(false); const showStyleSelectorView = ref(false);
</script> </script>

View File

@@ -25,6 +25,7 @@
:image="selectedImage" :image="selectedImage"
@close="currentOverlayComponent = null; selectedImage = null" @close="currentOverlayComponent = null; selectedImage = null"
@print="printImage" @print="printImage"
@download="downloadImage"
@changeStyle="showStyleSelector" @changeStyle="showStyleSelector"
@styleSelected="applyStyle" @styleSelected="applyStyle"
/> />
@@ -144,6 +145,42 @@ const printImage = () => {
currentOverlayComponent.value = 'printQuantityModal'; currentOverlayComponent.value = 'printQuantityModal';
}; };
const downloadImage = (image) => {
console.log('Starting download for image:', image);
// Show loading message
showError('Download wird vorbereitet...');
// Use axios to make a POST request to trigger backend download
axios.post('/api/download-image', {
image_path: image.path,
}, {
responseType: 'blob' // Important for file downloads
})
.then(response => {
// Create blob URL from response
const blob = new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
// Create temporary link and trigger download
const link = document.createElement('a');
link.href = url;
link.download = image.name || 'image';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// Clean up blob URL
window.URL.revokeObjectURL(url);
showError('Download gestartet!');
})
.catch(error => {
console.error('Error downloading image:', error);
showError(error.response?.data?.error || 'Download fehlgeschlagen.');
});
};
const handlePrintConfirmed = (quantity) => { const handlePrintConfirmed = (quantity) => {
console.log(`Printing ${quantity} copies of image:`, selectedImage.value); console.log(`Printing ${quantity} copies of image:`, selectedImage.value);
currentOverlayComponent.value = null; // Close the modal currentOverlayComponent.value = null; // Close the modal

View File

@@ -24,4 +24,7 @@ return [
'api.print_dialog.quantity_prompt' => 'Wie viele Kopien möchten Sie drucken?', 'api.print_dialog.quantity_prompt' => 'Wie viele Kopien möchten Sie drucken?',
'api.print_dialog.cancel_button' => 'Abbrechen', 'api.print_dialog.cancel_button' => 'Abbrechen',
'api.print_dialog.print_button' => 'Drucken', 'api.print_dialog.print_button' => 'Drucken',
'api.download_button' => 'Herunterladen',
'api.download_success' => 'Download gestartet!',
'api.download_error' => 'Download fehlgeschlagen.',
]; ];

View File

@@ -24,4 +24,7 @@ return [
'print_dialog.quantity_prompt' => 'How many copies would you like to print?', 'print_dialog.quantity_prompt' => 'How many copies would you like to print?',
'print_dialog.cancel_button' => 'Cancel', 'print_dialog.cancel_button' => 'Cancel',
'print_dialog.print_button' => 'Print', 'print_dialog.print_button' => 'Print',
'download_button' => 'Download',
'download_success' => 'Download started!',
'download_error' => 'Download failed.',
]; ];

View File

@@ -42,4 +42,6 @@ Route::middleware('auth:sanctum')->group(function () {
Route::get('/images/fetch-styled/{prompt_id}', [ImageController::class, 'fetchStyledImage']); Route::get('/images/fetch-styled/{prompt_id}', [ImageController::class, 'fetchStyledImage']);
use App\Http\Controllers\PrintController; use App\Http\Controllers\PrintController;
use App\Http\Controllers\DownloadController;
Route::post('/print-image', [PrintController::class, 'printImage']); Route::post('/print-image', [PrintController::class, 'printImage']);
Route::post('/download-image', [DownloadController::class, 'downloadImage']);