Antoine Vincent Antoine Vincent - 2 months ago 6
PHP Question

Swiftmailer Attachment error (unable to open file for reading) on symfony2 project

I'm working on a symfony project, and i have a form who have to send a mail with swiftmailer.
For the explication -> the user have the possibility to use attachment at optionnal file's upload function with the form. (the form is save in bdd like command, and we have a recap after submit the form, and 3mails that go (the two mail where we don't use attachment work every time, and only the one with atachment option have fail when we have a file to join, otherwise if we just use a commentary (an other field optionnal who work like path, the mail is working)

I hope my explainations are clear and comprehensible, my english is not perfect and the function is not too easy. Cause of the confidentiality of some informations i only write here the code who concerned the mailing but if i forget something just tell me and i update the post.

When i do a mail without attachment, i haven't any problem it's working, but when i try to join a file, symfony give me this error :


Unable to open file for reading [uploads/6M.jpg]


I have tried to get a solution on other post in StackOverflow but no one is working. So i'm gonna let you help me i Hope.

Command Entity (Ijust have put the code who concerne file , path and upload for the attachment (the rest of the mailer/and code work):


use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Form\Extension\HttpFoundation;

/**
* Commandes
*/
class Commandes
{
// CUSTOM CODE

/**
* @var string
*/
public $file3;

/**
* @var string
*/
protected $path3;

public function getAbsolutePath1()
{
return null === $this->path1 ? null : $this->getUploadRootDir().'/'.$this->id.'.'.$this->path1;
}

public function getWebPath1()
{
return null === $this->path1 ? null : $this->getUploadDir().'/'.$this->path1;
}

protected function getUploadRootDir()
{
return __DIR__ . '/../../../web/' . $this->getUploadDir();
}


protected function getUploadDir()
{
return 'uploads';
}


/**
* @var string
*/
protected $path1;


/**
* @Assert\File(maxSize="60000000")
*/
public $file1;


// CallBack : preUpdate | prePersist \\
public function preUpload1()
{
if (null !== $this->file1) {
$this->path1 = $this->file1->guessExtension();
}
}


// CallBacks : PostPersist | PostUpdate \\
public function upload1()
{
if (null === $this->file1) {
return;
}

$this->file1->move($this->getUploadRootDir(), $this->getReferencePatient() . 'M.' . $this->file1->guessExtension());

$this->path1 = $this->getReferencePatient() . 'M.' . $this->file1->getClientOriginalExtension();

$this->file1 = null;
}


protected $path2;

public $file2;

// CallBack : preUpdate | prePersist \\
public function preUpload2()
{
if (null !== $this->file2) {
$this->path2 = $this->file2->guessExtension();
}
}


// CallBacks : PostPersist | PostUpdate \\
public function upload2()
{
if (null === $this->file2) {
return;
}

$this->file2->move($this->getUploadRootDir(), $this->getReferencePatient() . 'm.' . $this->file2->guessExtension());

$this->path2 = $this->getReferencePatient() . 'm.' . $this->file2->getClientOriginalExtension();

$this->file2 = null;
}


public function getAbsolutePath2()
{
return null === $this->path2 ? null : $this->getUploadRootDir() . '/' . $this->path2;
}

public function getWebPath2()
{
return null === $this->path2 ? null : $this->getUploadDir() . '/' . $this->path2;
}


// CallBack : preUpdate | prePersist \\
public function preUpload3()
{
if (null !== $this->file3) {
$this->path3 = $this->file3->guessExtension();
}
}

public function upload3()
{
if (null === $this->file3) {
return;
}

$this->file3->move($this->getUploadRootDir(), $this->getReferencePatient() . '.' . $this->file3->guessExtension());

$this->path3 = $this->getReferencePatient() . '.' . $this->file3->getClientOriginalExtension();

$this->file3 = null;
}

public function getAbsolutePath3()
{
return null === $this->path3 ? null : $this->getUploadRootDir() . '/' . $this->path3;
}

public function getWebPath3()
{
return null === $this->path3 ? null : $this->getUploadDir() . '/' . $this->path3;
}

/**
* Set pathPJ1
*
* @param string $pathPJ1
* @return Commandes
*/
public function setPathPJ1($pathPJ1)
{
$this->pathPJ1 = $pathPJ1;

return $this;
}

/**
* Get pathPJ1
*
* @return string
*/
public function getPathPJ1()
{
return $this->pathPJ1;
}

/**
* Set pathPJ2
*
* @param string $pathPJ2
* @return Commandes
*/
public function setPathPJ2($pathPJ2)
{
$this->pathPJ2 = $pathPJ2;

return $this;
}

/**
* Get pathPJ2
*
* @return string
*/
public function getPathPJ2()
{
return $this->pathPJ2;
}

/**
* Set pathPJ3
*
* @param string $pathPJ3
* @return Commandes
*/
public function setPathPJ3($pathPJ3)
{
$this->pathPJ3 = $pathPJ3;

return $this;
}

/**
* Get pathPJ3
*
* @return string
*/
public function getPathPJ3()
{
return $this->pathPJ3;
}

/**
* Set path1
*
* @param string $path1
* @return Commandes
*/
public function setPath1($path1)
{
$this->path1 = $path1;

return $this;
}

/**
* Get path1
*
* @return string
*/
public function getPath1()
{
return $this->path1;
}

/**
* Set path2
*
* @param string $path2
* @return Commandes
*/
public function setPath2($path2)
{
$this->path2 = $path2;

return $this;
}

/**
* Get path2
*
* @return string
*/
public function getPath2()
{
return $this->path2;
}

/**
* Set path3
*
* @param string $path3
* @return Commandes
*/
public function setPath3($path3)
{
$this->path3 = $path3;

return $this;
}

/**
* Get path3
*
* @return string
*/
public function getPath3()
{
return $this->path3;
}


Commande Controller (same thing i put only the code about the attachment):

use Symfony\Component\HttpFoundation\Request;
use OrthoBundle\Entity\Compteur;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use OrthoBundle\Entity\Commandes;
use OrthoBundle\Form\Type\CommandesType;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\Session;

class FormulaireController extends Controller
{
public function createFormulaireAction(Request $request)
{
$em = $this->getDoctrine()->getManager();

$commande = new Commandes();

$request = $this->get('request');

$user = $this->getUser();

$form = $this->createForm(new CommandesType($user), $commande);

if ($form->isValid() && $form->isSubmitted()) {

***""""""SOME CODE THAT I DON'T PUT ABOUT PERSIST FLUSH THE COMMAND IN DB""""***

$message = \Swift_Message::newInstance()
->setSubject('Commande n°' . $commande->getId())
->setFrom(array('mymail@example.com' => 'send from'))
->setTo(array('onemail@example.com' => 'destinatory'))
->setCharset('utf-8')
->setContentType('text/html')
->setBody($this->renderView('@Ortho/Mail/mail_impress.html.twig', array(
'parentUser' => $user->getParent(),
'commande' => $commande,
)));
if ($commande->getWebPath1() != '') {
$message->attach(\Swift_Attachment::fromPath($commande->getWebPath1()));
}
if ($commande->getWebPath2() != '') {
$message->attach(\Swift_Attachment::fromPath($commande->getWebPath2()));
}
if ($commande->getWebPath3() != '') {
$message->attach(\Swift_Attachment::fromPath($commande->getWebPath3()));
}
if ($commande->getWebPath1() != '' or
$commande->getWebPath2() != '' or
$commande->getWebPath3() != '' or
$commande->getCommentaire() != '') {
$this->get('mailer')->send($message);
}


This code work for mail without attachment, but if i try to upload on form a file the mail don't go. For information : the file's uploads work and the uploaded file are save in MyApp/web/uploads.

CommandeType.php :

<?php

namespace MyBundle\Form\Type;

use MyBundle\Entity\Utilisateurs;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Doctrine\ORM\EntityRepository;

class CommandesType extends AbstractType
{
private $user;

public function __construct(Utilisateurs $utilisateurs)
{
$this->user = $utilisateurs;
}
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('commentaireLabo', TextareaType::class, array(
'attr' => array(
'placeholder' => 'Select a product'
),
))
->add('file1', FileType::class, array(
'required' => false
))
->add('file2', FileType::class, array(
'required' => false
))
->add('file3', FileType::class, array(
'required' => false
))
->add('commentairePrestataire', TextareaType::class, array(
'required' => false
));
}

/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyBundle\Entity\Commandes'

));
}


}

Here the part of the form in view who concerned file :

<fieldset class="smallbloc"> <!-- Bloc n°3 : Les pièces jointes -->
<h2 class="fs-title">Pièces jointes complémentaires</h2>
<h3 class="fs-subtitle">Toutes les pièces jointes à apporter à la commande.</h3>

{{ form_label(form.file1, "Arcade Maxilaire :") }}
{{ form_errors(form.file1) }}
{{ form_widget(form.file1) }}

{{ form_label(form.file2, "Arcade Mandibulaire :") }}
{{ form_errors(form.file2) }}
{{ form_widget(form.file2) }}

{{ form_label(form.file3, "Autre pièces jointe :") }}
{{ form_errors(form.file3) }}
{{ form_widget(form.file3) }}

{{ form_label(form.commentairePrestataire3D, "Commentaires pour le prestataire 3D :") }}
{{ form_errors(form.commentairePrestataire3D) }}
{{ form_widget(form.commentairePrestataire3D) }}

<input type="hidden" id="monthstat" name="monthstat" value="{{ "now"|date("m-Y") }}">

<input type="button" name="previous" class="previous action-button" value="Précédent"/>
<input type="submit" name="previous" class="action-button" value="ENVOYER"/>

{{ form_rest(form) }}
</fieldset>


I hope every necessary code are here, and that i haven't forget something, if not just said, and i'm gonna to update my post, and add the mising part. Or if i have said something brainless or who is not comprehensible just said me for explanations. I know it's not easy to read x). Thanks you every one for your help.

Answer

You must use the filesystem's absolute path of the file when attaching it, for instance :

$message->attach(\Swift_Attachment::fromPath($commande->getAbsolutePath1()));

SwiftMailer runs "server-side", it doesn't access your frontend to get the files you want to attach to your mail.

PS : You might also want to check that your file is actually present in the directory where it should be. Maybe the upload did not work as expected and the file is not present

Comments