I have a Symfony 3.3 contact form that sends an email. Now I am trying to add an attachment to the form. I insert the following line in my sendEmail function:
->attach($data["attachment"])
... and I get the following error:
Argument 1 passed to Swift_Mime_SimpleMessage::attach() must implement
interface Swift_Mime_MimeEntity, instance of
Symfony\Component\HttpFoundation\File\UploadedFile given
So my question is: How do I convert my UploadedFile object into something that SwiftMailer will be happy with?
====
Edit #1: I tried this with no success:
$fullFilePath = $data["attachment"]->getPath() . '/' . $data["attachment"]->getClientOriginalName();
$attachment = \Swift_Attachment::fromPath($fullFilePath);
Attaching that "attachment" just resulted in the email not being sent, though the application acted as if it had sent the form.
====
Edit #2: Progress! I'm now able to get a useful error. This code ...
$extension = $data["attachment"]->guessExtension();
if($extension !== 'rtf'){
die('Please give us an rtf file. TODO: Put a better message here!');
}
$newFilePath = '/tmp';
$newFileName = 'temporary.rtf';
$data["attachment"]->move($newFilePath, $newFileName);
... gives me an error like this:
Could not move the file "/tmp/phpnIqXDr" to "/tmp/temporary.rtf" ()
... which is very frustrating, since I know that /tmp is writeable by every user.
You don't need to move the file, Symfony\Component\HttpFoundation\File\UploadedFile class returns the path and has methods to get the filename and mimetype.
This code works for me:
$message->attach(
\Swift_Attachment::fromPath($data["attachment"])
->setFilename(
$data["attachment"]->getClientOriginalName()
)
->setContentType(
$data["attachment"]->getClientMimeType()
)
);
Credit to toolpixx
Here is the code that ended up working for me:
private function sendEmail($data)
{
$vgmsContactMail = self::contactMail;
$mailer = $this->get('mailer');
/* #var $uploadedFile UploadedFile */
$uploadedFile = $data["attachment"];
$extension = $uploadedFile->guessExtension();
if(!in_array($extension, ['pdf','rtf']) ){
die('Please upload a .pdf or .rtf file.');
}
$newFilePath = '/tmp';
$newFileName = 'temporary' . rand(0,10000) . '.rtf';
$uploadedFile->move($newFilePath, $newFileName);
$attachment = \Swift_Attachment::fromPath('/tmp/' . $newFileName);
$message = \Swift_Message::newInstance("VGMS Contact Form: ". $data["subject"])
->setFrom(array($vgmsContactMail => "Message by ".$data["name"]))
->setTo(array(
$vgmsContactMail => $vgmsContactMail
))
->setBody($data["message"]."<br>ContactMail :".$data["email"])
->attach($attachment)
;
return $mailer->send($message);
}
Related
I've recently created a page on our site where users can upload an image and email it to an email address set up specifically to keep the uploaded documents.
I've tested this myself and it works, with the attachments arriving in gmail as expected.
However, whenever someone from outside uses this feature the attachment in the email is unavailable, or not could not be loaded, when we try to open it.
The code is split between 2 files, a controller and a helper. Here's the code (For the sake of saving some space I've removed all error checks, but in the actual code they are all still in place and not picking up any errors whatsoever):
controller
$helper = [GET HELPER];
/** Upload the file to a temp location so that we can attach it to an email */
$uploader = new Varien_File_Uploader('filename');
$uploader->setAllowedExtensions(array(
'image/jpeg',
'image/jpg',
'image/png',
'application/pdf'
))
->setAllowRenameFiles(true)
->setFilesDispersion(false);
$path = $helper->getFileStorageLocation(); // Will store files in /tmp
if (!is_dir($path))
{
mkdir($path, 0775, true);
}
$uploader->save($path, $_FILES['filename']['name']);
$result = $helper->sendMail($_FILES['filename']['name']);
if ($result)
{
$uploadSuccess = true;
/** Remove the temp file */
unlink($path . DS . $_FILES['filename']['name']);
}
helper
/** Declare variables */
$order = Mage::getModel('sales/order')->load($orderId);
$file_incremented_id = $order->getIncrementId();
$copyTo = $this->getCopyTo();
$copyFrom = $this->getCopyFrom();
$subject = 'proof of upload for ' . $file_incremented_id;
$copyTo = explode(',', $copyTo);
$body = '<span>Please see attachment</span>';
$file = $this->getFileStorageLocation() . DS . $filename; // function receives filename from whatever is calling it
$attachment = file_get_contents($file);
$extension = pathinfo($file, PATHINFO_EXTENSION);
if (!$copyTo)
{
return false;
}
$mail = Mage::getModel('core/email_template');
$mail->setSenderName('Uploader');
$mail->setSenderEmail($copyFrom);
$mail->setTemplateSubject($subject);
$mail->setTemplateText($body);
$mail->getMail()->createAttachment(
$attachement,
Zend_Mime::TYPE_OCTETSTREAM,
Zend_Mime::DISPOSITION_ATTACHMENT,
Zend_Mime::ENCODING_BASE64,
$file_incremented_id . '.' . $extension // Set order number as file name
);
try
{
$mail->send($copyTo);
return true;
}
catch (Exception $e)
{
return false;
}
Can anyone see anything that might be causing the issue, or think of what it might be based on my explanation of the setup?
So the problem, in the end, was filesize. My fault for not posting the $_FILES variable.
I saw it a bit later and the variable had error = 1, meaning that the file's size was larger than what was allowed by the max_upload_filesize in the php.ini
I'm currently developing a form to upload a file to the server I'm working on. I can successfully upload a file to one of my app's folder without issue. What I want to do next is to read the file that was uploaded and add its info to a data base. To do this I'm using CakePHP's file API, but I can't get the constructor to work. Here is my code
<?php
App::uses('Component', 'Controller');
App::uses( 'String', 'Utility');
App::uses( 'File', 'Utility');
class UploadComponent extends Component{
public $max_files = 1;
public $filePath;
public function upload($data = null){
if( !empty( $data ) ){
if(count ($data) > $this->max_files){
throw new NotFoundException("Error procesando el pedido, el número máximo de archivos aceptados es {$this->max_files}", 1);
}
foreach($data AS $file){
$filename = $file['name'];
$file_tmp_name = $file['tmp_name'];
$dir = WWW_ROOT.'files'.DS.'uploads';
$allowed = array('txt');
if( !in_array( substr( strrchr( $filename, '.'), 1), $allowed)){
throw new NotFoundException("Error procesando el pedido", 1);
}elseif(is_uploaded_file( $file_tmp_name)){
move_uploaded_file($file_tmp_name, $dir.DS.String::uuid().$filename);
$filePath = $dir.DS.String::uuid().$filename;
echo gettype($filePath);
$import = new File($filePath); //this line throws an error
}
}
}
}
}
I'm confused as the API says that it receives a path in form of a string, which $filePath is, but it is saying that it's receiving an array. Any idea on how to make it work?
These are the errors the framework shows:
dirname() expects parameter 1 to be string, array given [CORE\Cake\Utility\File.php, line 87]
is_dir() expects parameter 1 to be a valid path, array given [CORE\Cake\Utility\File.php, line 88]
basename() expects parameter 1 to be string, array given [CORE\Cake\Utility\File.php, line 89]
Thanks in advance.
I see this errors in your code. Correct this and see if it solves the problem.
move_uploaded_file($file_tmp_name, $dir.DS.String::uuid().$filename);
$filePath = $dir.DS.String::uuid().$filename;
each time you call String::uuid() it gives you random uuid. You wont have filePath where you moved file.
It should be
$filePath = $dir.DS.String::uuid().$filename;
move_uploaded_file($file_tmp_name, $filePath);
Also you can use debug() function to see what value a variable has.
debug($filePath);
I'm uploading a file through Symfony2 and I am trying to rename original in order to avoid override the same file. This is what I am doing:
$uploadedFile = $request->files;
$uploadPath = $this->container->getParameter('kernel.root_dir') . '/../web/uploads/';
try {
$uploadedFile->get('avatar')->move($uploadPath, $uploadedFile->get('avatar')->getClientOriginalName());
} catch (\ Exception $e) {
// set error 'can not upload avatar file'
}
// this get right filename
$avatarName = $uploadedFile->get('avatar')->getClientOriginalName();
// this get wrong extension meaning empty, why?
$avatarExt = $uploadedFile->get('avatar')->getExtension();
$resource = fopen($uploadPath . $uploadedFile->get('avatar')->getClientOriginalName(), 'r');
unlink($uploadPath . $uploadedFile->get('avatar')->getClientOriginalName());
I am renaming file as follow:
$avatarName = sptrinf("%s.%s", uniqid(), $uploadedFile->get('avatar')->getExtension());
But $uploadedFile->get('avatar')->getExtension() is not giving me the extension of the uploaded file so I give a wrong filename like jdsfhnhjsdf. without extension, Why? What is the right way to rename file after or before move to the end path? Any advice?
Well, the solution is really simple if you know it.
Since you moved the UploadedFile, the current object instance cannot be used anymore. The file no longer exists, and so the getExtension will return in null. The new file instance is returned from the move.
Change your code to (refactored for clarity):
$uploadPath = $this->container->getParameter('kernel.root_dir') . '/../web/uploads/';
try {
$uploadedAvatarFile = $request->files->get('avatar');
/* #var $avatarFile \Symfony\Component\HttpFoundation\File\File */
$avatarFile = $uploadedAvatarFile->move($uploadPath, $uploadedAvatarFile->getClientOriginalName());
unset($uploadedAvatarFile);
} catch (\Exception $e) {
/* if you don't set $avatarFile to a default file here
* you cannot execute the next instruction.
*/
}
$avatarName = $avatarFile->getBasename();
$avatarExt = $avatarFile->getExtension();
$openFile = $avatarFile->openFile('r');
while (! $openFile->eof()) {
$line = $openFile->fgets();
// do something here...
}
// close the file
unset($openFile);
unlink($avatarFile->getRealPath());
(Code not tested, just wrote it) Hope it helps!
I made Joomla admin component according to Joomla guide - http://docs.joomla.org/Developing_a_Model-View-Controller_Component/2.5/Developing_a_Basic_Component
In that i need to have file uploader which let user to upload single file.
In administrator\components\com_invoicemanager\models\forms\invoicemanager.xml i have defined
<field name="invoice" type="file"/>
In the controller administrator\components\com_invoicemanager\controllers\invoicemanager.php im trying to retrieve that file like below. But its not working (can't retrieve file)
Where am i doing it wrong ?
How can i get file and save it on disk ?
class InvoiceManagerControllerInvoiceManager extends JControllerForm
{
function save(){
$file = JRequest::getVar( 'invoice', '', 'files', 'array' );
var_dump($file);
exit(0);
}
}
make sure that you have included enctype="multipart/form-data" in the form that the file is being submitting. This is a common mistake
/// Get the file data array from the request.
$file = JRequest::getVar( 'Filedata', '', 'files', 'array' );
/// Make the file name safe.
jimport('joomla.filesystem.file');
$file['name'] = JFile::makeSafe($file['name']);
/// Move the uploaded file into a permanent location.
if (isset( $file['name'] )) {
/// Make sure that the full file path is safe.
$filepath = JPath::clean( $somepath.'/'.strtolower( $file['name'] ) );
/// Move the uploaded file.
JFile::upload( $file['tmp_name'], $filepath );}
Think i found the solution :)
$file = JRequest::getVar('jform', null, 'files', 'array');
Saving part is mentioned here - http://docs.joomla.org/Secure_coding_guidelines
For uploading the file from your component, you need to write your code in the controller file and you can extend the save() method. check the code given below -
public function save($data = array(), $key = 'id')
{
// Neccesary libraries and variables
jimport('joomla.filesystem.file');
//Debugging
ini_set("display_error" , 1);
error_reporting(E_ALL);
// Get input object
$jinput = JFactory::getApplication()->input;
// Get posted data
$data = $jinput->get('jform', null, 'raw');
$file = $jinput->files->get('jform');
// renaming the file
$file_ext=explode('.',JFile::makeSafe($file['invoice']['name'])); // invoice - file handler name
$filename = round(microtime(true)) . '.' . strtolower(end($file_ext));
// Move the uploaded file into a permanent location.
if ( $filename != '' ) {
// Make sure that the full file path is safe.
$filepath = JPath::clean( JPATH_ROOT."/media/your_component_name/files/". $filename );
// Move the uploaded file.
if (JFile::upload( $file['invoice']['tmp_name'], $filepath )) {
echo "success :)";
} else {
echo "failed :(";
}
$data['name'] = $filename ; // getting file name
$data['path'] = $filepath ; // getting file path
$data['size'] = $file['invoice']['size'] ; // getting file size
}
JRequest::setVar('jform', $data, 'post');
$return = parent::save($data);
return $return;
}
Joomla 2.5 & 3 style:
$app = JFactory::getApplication();
$input = $app->input;
$file= $input->files->get('file');
if(isset($file['name']))
{
jimport('joomla.filesystem.file');
$file['name'] = strtolower(JFile::makeSafe($file['name']));
$fileRelativePath = '/pathToTheRightFolder/'.$file['name'];
$fileAbsolutePath = JPath::clean( JPATH_ROOT.$fileRelativePath);
JFile::upload( $file['tmp_name'], $fileAbsolutePath );
}
http://docs.joomla.org/How_to_use_the_filesystem_package
has a full upload sample.
Little sample where admin choose the file type or all, enter the users to access the form upload. Folder to upload files in Joomla directory or with absolute path. Only selected users access the form upload.
i have an issue with uploading multiple files to disk. here is my code.
i have a request with 2 pictures that gets sent to a upload function. the 2 pictures are in a var called $multiUpload
$folderPath = '/var/www/';
if (is_array($multiUpload)){
$file = array();
$filename = array();
foreach($multiUpload as $key=>$val){
// get the file extension
$file[] = explode('.',$val);
// create custom file name
$filename[] = time().'.'.$file[$key][1];
//send to the upload function
$this->uploadToDisk($folderPath, $filename[$key]);
// sleep 1 sec so that the pic names will be different
sleep(1);
}
return $filename;
}
public function uploadToDisk($folderPath, $filename)
{
$adapter = new Zend_File_Transfer_Adapter_Http();
$adapter->setDestination($folderPath);
$adapter->addFilter( 'Rename',array(
'target' => $folderPath."/".$filename,
'overwrite' => true
) );
if ($adapter->receive()) {
$message = "success";
} else {
$message = "fail";
}
return $message;
}
this will return
Array
(
[0] => Array
(
[0] => 1332977938.jpg
[1] => 1332977939.jpg
)
)
but only array[0][0] or 1332977938.jpg will actually get saves to the disk.
Why are they now both get saved? wired
any ideas?
I suspect the second call to uploadToDisk is returning fail because you can only call Zend_File_Transfer_Adapter_Http::receive() once for each file. Since you are not specifying a file when calling receive, it is receiving all of the files the first time you call uploadToDisk and subsequently is failing with a File Upload Attack error.
Here is some code you can try. This tries to receive each file individually and then save them one at a time with each call to uploadToDisk.
A few notes about the code:
The first parameter to uploadToDisk ($val) may need to be changed as I am not sure what the original values are. It should correspond to one of the element names used for the file upload (See Zend_File_Transfer_Adapter_Http::getFileInfo()) for a list of the files.
I changed the method for generating a unique filename so you don't have to sleep(1)
Zend_File_Transfer_Adapter_Abstract::setDestination() is deprecated and will go away in the future. Instead, just use the Rename filter. When using Rename, setDestination() has no effect.
And here it is...
<?php
$folderPath = '/var/www/';
if (is_array($multiUpload)){
$filenames = array();
foreach($multiUpload as $key => $val){
// get the file extension
$ext = explode('.', $val);
$ext = $ext[sizeof($ext) - 1];
// create custom file name
do {
$filename = uniqid(time()) . '.' . $ext;
$diskPath = $folderPath . $filename;
} while (file_exists($diskPath));
$filenames[$key] = $filename;
//send to the upload function
// $val is the file to receive, $diskPath is where it will be moved to
$this->uploadToDisk($val, $diskPath);
}
return $filename;
}
public function uploadToDisk($file, $filename)
{
// create the transfer adapter
// note that setDestination is deprecated, instead use the Rename filter
$adapter = new Zend_File_Transfer_Adapter_Http();
$adapter->addFilter('Rename', array(
'target' => $filename,
'overwrite' => true
));
// try to receive one file
if ($adapter->receive($file)) {
$message = "success";
} else {
$message = "fail";
}
return $message;
}