Download link for files in storage not working - Laravel - php

I am putting my files in storage, and it is working. However, the download link is returning an error.
Here is the view which allows users to upload the files
<div class="form-group{{ $errors->has('notes') ? ' has-error' : '' }}">
<label class="col-md-4 control-label">Upload your notes</label>
<div class="col-md-6">
<input type="file" class="form-control" name="notes">
#if ($errors->has('notes'))
<span class="help-block">
<strong>{{ $errors->first('notes') }}</strong>
</span>
#endif
</div>
</div>
Here is my handling of the request in a controller.
Storage::put(
'notes/' . $note->id . '.pdf',
file_get_contents($request->file('notes')->getRealPath())
);
Everything if firing and the pdf files are being stored in my storage/app/notes directory.
The link I am using to download the files is: /notes/90.pdf, where '90' is the note id.
I have been getting the error
NotFoundHttpException in RouteCollection.php line 161:

You will need to define a route that can serve your file.
Route::get('notes/{id}', function ($id) {
return response()->download(storage_path('notes/' . $id . 'pdf'));
});
You might want to move the download logic to a controller. And add validations to check if the file exists or not but you get the basic idea.

Related

The POST method is not supported for this route. Supported methods: GET, HEAD. Laravel 9 [duplicate]

This question already has answers here:
The POST method is not supported for this route. Supported methods: GET, HEAD. Laravel
(19 answers)
Closed 8 months ago.
First off, I'm a beginner and I know this question has been asked a few times, and answered. I've attempted the solutions, or I've had the suggestion implemented, so I've come here for help from the experts!
What I've tried:
-Adding #csrf to my blade file inside my form element.
-Checking that my post and get's in my routes are lowercase.
-Changing the gets to post and so fourth.
The above has not helped in solving the problem, and alas I'm still scratching my head.
Effectively, I'm trying to upload an image, give the image a caption and submit. It should go to another page displaying the data in a drop down in the top left hand corner, but I'm greeted with the image in the title.
EDIT: Solution: I ran the following and it removed the error:
php artisan route:clear
I've included images below of the error and my folder structure:
Image of error appearing.
Project directory structure
Here's the code:
Web.php
<?php
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/p/create', 'App\Http\Controllers\PostsController#create');
Route::post('/p/', 'App\Http\Controllers\PostsController#store');
Route::get('profile/{user}', 'App\Http\Controllers\ProfilesController#index')-
>name('profile.show');
PostsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class PostsController extends Controller
{
public function constructor()
{
$this->middleware['auth'];
}
public function create()
{
return view('posts.create');
}
public function store() {
$data = request()->validate([
'caption' => 'required',
'image' => 'required|image',
]);
dd(request('image')->store('uploads','public'));
auth()->user()->posts()->create($data);
dd(request()->all());
}
}
Model/Posts.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
}
views/posts/create.blade.php
#extends('layouts.app')
#section('content')
<div class="container">
<form action="/p" enctype="multipart/form-data" method="post">
#csrf
<div class="row">
<div class="col-8 offset-2">
<div class="row mb-3">
<div class="row pt-5">
<h1>Add New Post</h1>
</div>
<label for="caption" class="col-md-4 col-form-label">Post Caption</label>
<input id="caption"
type="text"
class="form-control #error('caption') is-invalid #enderror"
name="caption"
value="{{ old('caption') }}"
autocomplete="caption" autofocus>
#error('caption')
<strong>{{ $message }}</strong>
#enderror
</div>
<div class="row">
<label for="image" class="col-md-4 col-form-label">Post Image</label>
<input type="file" class="form-control-file" id="image" name="image">
#error('image')
<strong>{{ $message }}</strong>
#enderror
<div class="pt-3">
<button class="btn btn-primary btn-sm">Add New Post</button>
</div>
</div>
</div>
</div>
</form>
#endsection
Thank you in advance for your help!
Could this be caused by your route being defined as "/p/", while your form action is just "/p" (without the trailing slash)? I'd try ensuring those are an exact match first. Change your route to say:
Route::post('/p', 'App\Http\Controllers\PostsController#store');
You could also try broadening that route definition.
Route::post('/p/', 'App\Http\Controllers\PostsController#store');
would become
Route::any('/p/', 'App\Http\Controllers\PostsController#store');
EDIT: Solution: I ran the following and it removed the error:
php artisan route:clear
you need to use and name for your router, and now route will look like
Route::post('p',[PostsController::class, 'store'])->name('p.name');
And action form:
<form action="{{route('p.name')}}" enctype="multipart/form-data" method="post">
And clear cache route then change:
php artisan route:cache

How can I upload image in local folder laravel?

I am new in larval development, I am unable to upload image in local folder and I am getting error getClientOriginalExtension() on null. Even I have added code enctype="multipart/form-data" in my form, but still am getting the error. Can anyone give suggestion how to resolve this issue. I have given below my code.
MyBlade file
****MyBlade file****
<form action="{{ route('register.post') }}" method="POST" enctype="multipart/form-data" accept-charset="utf-8" >
#csrf
<div class="form-group row">
<label for="uname" class="col-md-4 col-form-label text-md-right">User Name</label>
<div class="col-md-6">
<input type="text" id="username" class="form-control" name="username" required />
#if ($errors->has('username'))
<span class="text-danger">{{ $errors->first('username') }}</span>
#endif
</div>
</div>
<div class="form-group row{{ $errors->has('image') ? ' has-error' : '' }}">
<label for="name" class="col-md-4 col-form-label text-md-right">Profile Picture</label>
<div class="col-md-6">
<input id="image" type="file" class="form-control" name="image">
</div>
</div>
My Controller File
if ($request->input('image')!=null){
$files = $request->input('image');
$extension = $files->getClientOriginalExtension();
$filaname = time().','.$extension;
$files->move('public/image/',$filaname);
// $post->image= $filaname;
}
else{
return $request;
}
I am getting err:Call to a member function getClientOriginalExtension() on bool
If you use $request->file('image') instead of $request->input('image') you have fixed the problem. The image will then be moved to the public/image folder.
However, if you want to upload the file to the storage/public folder, you can use the storeAs() method on the $file to store it in the storage folder.
// Your controller
public function update(Request $request, $postId)
{
if (! $request->hasFile('image')) {
return $request;
}
// Use file() instead of input()
$file = $request->file('image');
$extension = $file->getClientOriginalExtension();
// Note you have a typo in your example, you're using a `,` instead of a `.`
$filename = time().'.'.$extension;
// To store (move) the file to the 'public/image' folder, use this:
$file->move('image', $filename);
// To store the file to the 'storage/app/public/image' folder, use this:
$file->storeAs('public/image', $filename);
// Find your post based on some id
$post = Post::find($postId);
// Save the filename to the database on the Post model:
$post->image = $filename;
$post->save();
}
Hope I helped you with this.

Using #error directive to target the previous input field from multiple input fields after submit in Laravel 5.8

The new #error directive was introduced in Laravel 5.8.13. So, instead of doing this:
// old
#if ($errors->has('email'))
<span>{{ $errors->first('email') }}</span>
#endif
You can now do this
// new
#error('email')
<span>{{ $message }}</span>
#enderror
However, I'm having issues trying to target only the input fields where the error was made among several similar input fields. These fields are similar because they have the same name. But they are also in seperate forms and have different submit buttons.
My html blade file is setup with multiple inputs generated from a #forelse statement. I've created a simple error check to check if a negative number is entered and to redirect back to the same page with an error message. The #error directive is also used to insert a custom class to target input field for styling.
#forelse($accounts as $account)
<form method="POST" action="{{ route('account.update', $account->id) }}">
#csrf
<div>
<input type="number" name="credit" class="#error('credit') is-invalid #enderror" required>
<input type="submit" class="btn" value="ok">
#error('credit')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</form>
#empty
#endforelse
The form submits to the update() method in AccountController.php
public function update(Request $request, Account $account)
{
if($request->input('credit') < 0)
{
return back()->withErrors(['credit'=>'enter a positive amount']);
}
// ... other logic
}
The problem is that when a negative number is entered in one input field, the error message shows up for every input with the same name, even when they are not in the same form.
I think making the input names unique will solve this, but this will make my backend logic more complex than is required.
Is there anyway of making the error message show for just the target input after the redirect, without having to use unique names for each input field?
Interestingly, the #error directive is not programmatically tied to any input field by an input name. It is only spatially related to an input field by proximity and can be placed any where on the page.
Also, you can conveniently use any string within the #error directive as long as same is passed to the withErrors() call in the controller.
So, solving the problem of targeting the appropriate input among multiple ones becomes as simple as using any unique string (not necessarily the target input name) as key in the withErrors() method call and retrieving the error message by passing the same string to the #error directive. In my case, I chose to use the account id as the unique string.
In AccountController.php:
public function update(Request $request, Account $account)
{
if($request->input('credit') < 0)
{
return back()->withErrors([$account->id=>'enter a positive amount']);
}
// ... other logic
}
And in the html blade template:
#forelse($accounts as $account)
<form method="POST" action="{{ route('account.update', $account->id) }}">
#csrf
<div>
<input type="number" name="credit" class="#error($account->id) is-invalid #enderror" required>
<input type="submit" class="btn" value="ok">
#error($account->id)
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
#enderror
</div>
</form>
#empty
#endforelse
NOTE: Using a non existent key in any #error directive breaks the code for other #error directives and will cause no error messages to be displayed.
A solution would be doing the following:
store the value / posted form in a variable using the value of the submit button
ditch the error directive and use the old version
combine the old version with the variable to check wether you have the correct form or not
You'll get something like this:
#if ($errors->has('email') && $submitted_form_identifier === $form_identifier)
<span>{{ $errors->first('email') }}</span>
#endif

Display a link if an item has a PDF in laravel

I am trying to get an item to display a link to PDF if that item has a PDF associated with it. I am not sure how the if statement would be formatted. I am using Laravel for this.
Here is the code that I have in the controller for the item:
//PDF Upload
if($request->hasFile('spec_sheet')) {
//Get file name and extension
$filenameWithExt = $request->file('spec_sheet')->getClientOriginalName();
//Get just file name
$filename = pathinfo($filenameWithExt, PATHINFO_FILENAME);
//Get Ext
$extension = $request->file('spec_sheet')->getClientOriginalExtension();
//Store file name
$fileNameOfPdfToStore = $filename. '.' .$extension;
//Upload
$path = $request->file('spec_sheet')->storeAs('public/mcelroy-specs', $fileNameOfPdfToStore);
}
$rental = new Rental;
$rental->title = $request->title;
$rental->name = $request->name;
$rental->description = $request->description;
$rental->product_image = $fileNameToStore;
$rental->spec_sheet = $fileNameOfPdfToStore;
$rental->save();
In the view I have this:
#foreach($rentals as $rental)
<article class="cards-item">
<figure class="card product">
<div class="card-images">
<img src="/storage/rentals/{{$rental->product_image}}" alt="" >
</div>
<figcaption class="card-content">
<h1 class="card-title">{{ $rental->title }}</h1>
<h2 class="card-title product_title">{{ $rental->name }}</h2>
<p class="card-text" style="text-align:left;">{{ $rental->description }}
#if(!empty($spec_sheet))
Download Spec Sheet
#endif
</p>
<form action="{{ route('cart.store') }} " method="POST">
{{ csrf_field() }}
<input type="hidden" name="id" value="{{ $rental->id }}">
<input type="hidden" name="title" value="{{ $rental->title }}">
{{-- <input type="hidden" name="pickupDate" value="{{ $rental->pickupDate }}">
<input type="hidden" name="returnDate" value="returnDate"> --}}
<div class="buttons">
<div class="back">
<button class="primary button" type="submit">Add to Cart</button>
</div>
</div>
</form>
</figcaption>
</figure>
</article>
#endforeach
Seems like you are looking for the right way to do something in Laravel, but there is no right way.
Given
#if(!empty($spec_sheet))
Download Spec Sheet
#endif
I would make two changes, 1) shouldn't $spec_sheet be $rental->spec_sheet? and 2) swap out the #if(!empty(...)) with #unless(empty()) as I feel that including a ! will make it harder to understand when you come back to this in a few weeks.
So I am suggesting you use this which should be just fine, since there's no right way to do this in Laravel.
#unless(empty($rental->spec_sheet))
Download Spec Sheet
#endunless
Your code above will not work if there is no PDF attached, as the variable $fileNameOfPdfToStore will not exist and a hard error will be thrown. But assuming you fix that issue and store an empty string for the file name or perhaps NULL instead of the file path, then you could create a new attribute in the Rental model that looks like this:
public function getHasSpecSheetAttribute() {
return $this->spec_sheet==null? false:true;
}
Then in your view you would do this:
#if($rental->hasSpecSheet)
Download Spec Sheet
#endif
In order to fix the PDF upload code, just put an else statement and put this inside of it:
$fileNameOfPdfToStore = null;
#if(strlen({$rental->spec_sheet) > 0))
Download Spec Sheet
#endif
use string length to determine if there was file name uploaded or not

homestead laravel 5 : link Download file from storage

i am need hint link to download upload file, currently i am following this answer
here is my code
Routes file :
Route::get('getDownload/{remind_letter}',
['as'=>'downloadData',
'uses'=>'DockController#getDownload']);
Controller File :
public function getDownload($remind_letter)
{
$download = Dock::where('remind_letter','=',$remind_letter)->firstOrFail();
$file =Storage::disk('local')->get($download->remind_letter);
return (new response($file))->header('Content-Type', $entry->m1);
}
result file
<div class="row">
<div class="form-group">
<div class="col-xs-5">
<label class="col-sm-7">Remind Letter :</label>
<input type="text" name="remind_letter" value="{{$getData->remind_letter}}">
</div>
<div><a href="{{ route('downloadData',$getData->remind_letter) }}">
<button class="btn btn-success btn-sm">Download</button></a></div>
</div>
</div>
and the result link dock.start/star/getDownload/home/vagrant/Code/dock/storage/app/25/JTK/2015/Renewal License Kaspersky/Moonarch Security/tmp/phpQaGzoD.pdf
but the link is always show up error 404 and i can't download file
FYI when upload file i am using storage_path() function
it fix, i am wrong logic, when upload file it should using getFilename() function, $entry->filename = $filename->getFilename().$extension;

Categories