Getting mimes validation fail when not uploading optional file with AJAX

Published 4 weeks ago by Gabotronix

Hi, I'm trying to make a bug report system where the user sends a body of text and OPTIONALLY he can upload a file (image, doc...) along with the text to be sent to my mail inbox.

However, when I only send text I get this error:

"The file must be a file of type: doc, pdf, docx, zip, odt, jpeg, bmp, png, jpg, svg."

how can I fix this, also when I upload a file along with text the mail is sent all right but the attached file is only 1kb, always.

Jquery code:

var formData = new FormData();
        formData.append('body', $('#bug_body').val());
        formData.append('file', $('#bug_file').prop('files')[0]);
        
        
        $.ajaxSetup({
            
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf_token"]').attr('content')}
        
        });
        
        $.ajax({
            
            async: true,
            url: '/contactar-mail-bug',
            type: 'POST',
            data: formData,
            contentType: false,
            processData: false, 
            dataType: 'JSON',
            
            success: function (data) { 
                $('.form_valid_container').fadeIn().html('<span class="form_valid_text">✓ '+ data.success +'</span>');
                form.trigger("reset");
                console.log(data.success);
            },
            
            error: function (data){
                var errors = data.responseJSON;
                console.log(errors);
                
                $.each(errors , function(){
                    $('.form_error_container').fadeIn().html('<span class="form_error_text">✘ '+ errors.message +'</span>')
                }); 
            }

        });

Method for sending email:

public function contactar_reportar_bug(BugReport $request)
    {
        $sender_mail = config('globals.admin_mail');//mail sender, in this case it's me
        
        $destination_mail = config('globals.admin_mail');//mail receiver, in this case it's me too
        
        $client_phone = config('globals.client_phone');
        
        $body = $request->body;//bug report mail message
        
        $file = $request->file('file');//bug report screencap or doc
        
        $file_name = $file->getClientOriginalName();//outputs Laravel DOC.odt
        
        $mime_type = $file->getMimeType();//outputs application/
        
        $file_extension = $file->guessExtension();//outputs .odt
        
        $restaurant = Globals::where('name' , 'Nombre del restaurante')->pluck('value');//outputs web_name, used in the mail subject
        
        $subject = "Nuevo reporte de bug en $restaurant";//subject of the mail
        
        
        
        
        $bug = new Bug();
        $bug->body = $request->body;
        $bug->save();
        
        
        
        Mail::to($destination_mail)->send(new BugReportMail($body, $file, $subject, $restaurant, $client_phone, $sender_mail, $file_name, $mime_type));
        
        return response()->json([
        
            'success' => 'Reporte de bug enviado',
            'bug' => $bug,

        ]);
    }

Bug Report form request (notice how only the body of the message is required):

public function rules()
    {
        return [
            
            'body' => 'required|string|min:5',
            
            'file' => 'mimes:doc,pdf,docx,zip,odt,jpeg,bmp,png,jpg,svg|max:2000'
        ];
    }
lostdreamer_nl

You cannot use ajax to simply POST a file to the server, file uploads are very different from data POST's.

Check this tutorial if you really want to use ajax for the file upload : http://www.ajaxf1.com/tutorial/ajax-file-upload-tutorial.html

Gabotronix

Well I'm not saving the file in the app, I just want to attach it to a mail

lostdreamer_nl

but before that, it needs to be uploaded to the server, which you cannot do by using a normal AJAX request

Gabotronix

Oh, so there's no way to attach a file to mail without saving it in storage then?

lostdreamer_nl

there is, (you can keep the file in memory instead of write it to the storage) but you still need to send the file to the server (upload), which cannot be done (easily) via ajax.

Uploading does not necessarily mean that the server has to write the file to disk, it only means that a client sends a file to a server, and that is where you are stuck now because of the ajax request.

Cronix
Cronix
4 weeks ago (647,500 XP)

Try adding a nullable rule to your file rules.

You should also check whether the request has a file to process, before just blindly trying to process it.

if ($request->has('file')) {
    // do the processing for $request->file
}
Gabotronix

Ok so I added nullable to my FormRequest:

public function rules()
    {
        return [
            
            'body' => 'required|string|min:5',
            
            'file' => 'nullable|mimes:doc,pdf,docx,zip,odt,jpeg,bmp,png,jpg,svg|max:2000'
        ];
    }

I also made quite a few changes to my controller method:

public function contactar_reportar_bug(BugReport $request)
    {
        
        $bug = new Bug();
        $bug->body = $request->body;
        $bug->save();
        
        
        if ($request->has('file')) {
            $uploadFile = $request->file('file');
            $mimetype = $uploadFile->getMimeType();
            $filename = str_random(6).'.'.$uploadFile->extension();
            $uploadFile->storeAs('uploads', $filename);
        }
        
        $sender_mail = config('globals.admin_mail');
        
        $destination_mail = config('globals.admin_mail');
        
        $client_phone = config('globals.client_phone');
        
        $body = $request->body;
        
        $restaurant = Globals::where('name' , 'Nombre del restaurante')->pluck('value');
        
        $subject = 'Nuevo reporte de bug en '.$restaurant;
        
        
        
        Mail::to($destination_mail)->send(new BugReportMail($body, $uploadFile, $filename, $mimetype, $subject, $restaurant, $client_phone, $sender_mail));
        
        return response()->json([
        
            'success' => 'Reporte de bug enviado',
            'bug' => $bug,

        ]);
    }

But I'm still getting the same mime related error message

Cronix
Cronix
4 weeks ago (647,500 XP)

You will still have a problem in your mail function, because you are sending $uploadFile to it, which won't exist unless the file exists since it only gets defined within the file check

if ($request->has('file')) {
    $uploadFile = $request->file('file');
    $mimetype = $uploadFile->getMimeType();
    $filename = str_random(6).'.'.$uploadFile->extension();
    $uploadFile->storeAs('uploads', $filename);
}

I'd add a $uploadFile = null; just before that file check, and then also need to check if it's null within your mailable view.

This is a hack and might not be the best solution, but it will probably work.

public function rules()
{
    $rules = [
        'body' => 'required|string|min:5',
    ];

    if (request()->has('file')) {
        $rules['file'] = 'nullable|mimes:doc,pdf,docx,zip,odt,jpeg,bmp,png,jpg,svg|max:2000';
    }

    return $rules;
}

Maybe someone has a better solution, but I'm just adding the mimecheck if there is a file present in the request.

Gabotronix

Will report back once I get home, thx

Gabotronix

Welp, I'm still getting the same mime validation error, I changed my form request:

 public function rules()
    {
        $rules = [
            'body' => 'required|string|min:5',
        ];

        if (request()->has('file')) {
            $rules['file'] = 'nullable|mimes:doc,pdf,docx,zip,odt,jpeg,bmp,png,jpg,svg|max:2000';
        }

    return $rules;
    }

And added the file check to my method controller:

public function contactar_reportar_bug(BugReport $request)
    {
        
        $bug = new Bug();
        $bug->body = $request->body;
        $bug->save();
        
        $uploadFile = null; 
        
        if ($request->has('file')) {
            $uploadFile = $request->file('file');
            $mimetype = $uploadFile->getMimeType();
            $filename = str_random(6).'.'.$uploadFile->extension();
            $uploadFile->storeAs('uploads', $filename);
        }
        
        $sender_mail = config('globals.admin_mail');
        
        $destination_mail = config('globals.admin_mail');
        
        $client_phone = config('globals.client_phone');
        
        $body = $request->body;
        
        $restaurant = Globals::where('name' , 'Nombre del restaurante')->pluck('value');
        
        $subject = 'Nuevo reporte de bug en '.$restaurant;
        
        
        
        Mail::to($destination_mail)->send(new BugReportMail($body, $uploadFile, $filename, $mimetype, $subject, $restaurant, $client_phone, $sender_mail));
        
        return response()->json([
        
            'success' => 'Reporte de bug enviado',
            'bug' => $bug,

        ]);
    }

And as recommended by Cronix I edited my mailable class to check if a file exists:

public function build()
    {
        
        if( is_null($this->uploadFile))
        {
            
            return $this->markdown('emails.bug_reports')
            ->from($this->sender_mail, 'BugReportGab')
            ->subject($this->subject);
            
        }
        
        
        return $this->markdown('emails.bug_reports')
        ->from($this->sender_mail, 'BugReportGab')
        ->subject($this->subject)
        ->attach($this->uploadFile, [
            'as' => $this->filename,
            'mime' => $this->mimetype,
        ]);
    }
lostdreamer_nl

Then stop sending the file as a text field via ajax:

As i said before, you cannot upload a file via ajax that way, you can do it in a plain XMLHttpRequest but not via the jquery $.ajax function

So either remove this line:

        formData.append('file', $('#bug_file').prop('files')[0]);

Or change your javascript upload to be able to actually upload that file. Check here for an example of how you can do a file upload via $.ajax()

https://abandon.ie/notebook/simple-file-uploads-using-jquery-ajax

Please sign in or create an account to participate in this conversation.