I am currently using symfony 1.4 and would like to allow users to upload Microsoft Word docx files. Using the sfWidgetFormInputFile widget and sfValidatorFile below users are able to select and successfully upload their docx files using a simple web form.
$this->widgetSchema['file_name'] = new sfWidgetFormInputFile(array('label' => 'File'));
$this->validatorSchema['file_name'] = new sfValidatorFile(array(
'required' => true,
'path' => sfConfig::get('sf_upload_dir').DIRECTORY_SEPARATOR.sfConfig::get('app_dir_file_sharing').DIRECTORY_SEPARATOR,
'mime_types' => array('application/msword',
'application/vnd.ms-word',
'application/msword',
'application/msword; charset=binary')
), array(
'invalid' => 'Invalid file.',
'required' => 'Select a file to upload.',
'mime_types' => 'The file must be a supported type.'
));
The problem is that after the file is uploaded, the extension is changed to .zip and the file contains a file tree of xml files. My understanding is that this is because Office 2007 are now using Open xml file formats. Is there any way to prevent this from happening using symfony or PHP?
The problem is Content-Sniffing. The new Office formats ARE .zip files, and if on upload, the content is sniffed, the browser will identify this as a ZIP file and set the Content-Type header as such. Similarly, on download unless your server sets the proper Content-Type HTTP response header, the browser will assume that this is a ZIP file.
Symfony 1.3+ has an option mime_type_guessers for sfValidatorFile which allows you to define your own mime type guesser PHP callable or use a build in guesser. Calling any of the 3 built-in mime type guessers finds the correct file type for docx and keeps the the docx file extension.
Here is the updated code using guessFromFileinfo:
$this->validatorSchema['file_name'] = new sfValidatorFile(array(
'required' => true,
'path' => sfConfig::get('sf_upload_dir').DIRECTORY_SEPARATOR.sfConfig::get('app_dir_file_sharing').DIRECTORY_SEPARATOR,
'mime_type_guessers' => array('guessFromFileinfo'),
'mime_types' => array('application/msword',
'application/vnd.ms-word',
'application/msword',
'application/msword; charset=binary')
), array(
'invalid' => 'Invalid file.',
'required' => 'Select a file to upload.',
'mime_types' => 'The file must be a supported type.'
));
It seems to be a bug in Symfony's file type detection. A workaround is described.
The suggested use of mime_type_guessers uses a non-existing function.
If you want to use the sfValidatorFile method, you should write array(array('sfValidatorFile', 'guessFromFileinfo')).
The suggested solution uses no mime-type detection at all and results in problems with the classic excel format on my system.
I fixed the problem by extending the sfValidatorFile class and changing the getMimeType method.
Create a new msValidatorFile.class.php file in your lib folder :
<?php
class msValidatorFile extends sfValidatorFile
{
protected function getMimeType($file, $fallback)
{
$arrayZips = array( "application/zip",
"application/x-zip",
"application/x-zip-compressed");
$officeTypes = array(
"application/vnd.ms-word.document.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
"application/vnd.ms-powerpoint.template.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.presentationml.template",
"application/vnd.ms-powerpoint.addin.macroEnabled.12",
"application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
"application/vnd.ms-powerpoint.presentation.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.ms-excel.addin.macroEnabled.12",
"application/vnd.ms-excel.sheet.binary.macroEnabled.12",
"application/vnd.ms-excel.sheet.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-excel.template.macroEnabled.12",
"application/vnd.openxmlformats-officedocument.spreadsheetml.template");
foreach ($this->getOption('mime_type_guessers') as $method)
{
$type = call_user_func($method, $file);
if (null !== $type && $type !== false)
{
if (in_array($type, $arrayZips) && in_array($fallback, $officeTypes))
{
return $fallback;
}
return strtolower($type);
}
}
return strtolower($fallback);
}
}
Use this new validator in your form code :
$this->validatorSchema['file'] =
new msValidatorFile(array('required' => false,
'path' => sfConfig::get('sf_upload_dir')
));
Related
on output when open page -
File ID: 1qG8tteyVhAbB_rbu_VUvaE9ReqnSjEAh...
But on google drive no new files are created. I want upload file to cron but now i want only download test.pdf and end.
require_once './google-api-php-client/vendor/autoload.php';
use Google\Client;
use Google\Service\Drive;
function uploadBasic()
{
try {
$client = new Client();
//$client->useApplicationDefaultCredentials();
$client->setAuthConfig('./google-api-php-client/1710-6c50418be6b2.json');
$client->addScope(Drive::DRIVE);
$driveService = new Drive($client);
$fileMetadata = new Drive\DriveFile(array(
'parents' => ['225qhcKKyf8Ot0IhrRxRtqgHNTxLV1LiyI'],
'name' => 'test.pdf',
'mimeType' => 'application/pdf'));
$mimeType=mime_content_type($fileMetadata);
$content = file_get_contents('https://example.com/test.pdf');
$file = $driveService->files->create($fileMetadata, array([
'data' => $content,
'mimeType' => 'application/pdf',
'uploadType' => 'multipart',
'fields' => 'id']));
printf("File ID: %s\n", $file->id);
return $file->id;
} catch(Exception $e) {
echo "Error Message: ".$e;
}
}
uploadBasic();
how to debug issue
The fastest way to debug this is to do a File.list. This will tell you if in fact the file was uploaded.
You are not setting parents yin your meta data, so the file will have been uploaded to the root directory.
service account
Remember if you are using a service account that the files are uploaded into the service accounts google drive account, not your personal drive account.
To upload to your personal drive account you would need to create a directory on your drive account, share that directory with your service account using the service account email address. The service account email address can be found in the json key file its the only one with an #.
Then set parents in the meta data to the folder on your drive account
$fileMetadata = new Drive\DriveFile(array(
'parents' => { 'FolderId' }
'name' => 'ASB-Background-3.png'));
File 0 size error after edit
You edited your question. It originally stated you were doing this
$content = file_get_contents('./google-api-php-client/ASB-Background-3.png');
It is bad practice to update your question and change your code. It changes the answer to your question and in this case your error message.
That being said From the documentation for file_get_contents
file_get_contents — Reads entire file into a string
There is nothing in the documentation that states that this method could load a file from a url. So your edit changing to a URL is probably not going to work.
file_get_contents('https://example.com/test.pdf');
Your file is uploading with 0 because your not giving it a file. Download that file on to the machine running it and then send it, or write our own method which will accept a url and return a string file conents.
upload image
Files are uploaded in two parts first the fileMetadata and then the file itself.
MimeType must be properly set to that of the file you are uploading. file_get_contents will only work on a file that is currently accessible by your code.
If the file size is 0 make sure
to check the most recent uploaded file. every create will create a new file.
ensure that your code has access to the file you are uploading.
make sure the mimeType is correct.
Sample.
try {
$client = new Client();
$client->useApplicationDefaultCredentials();
$client->addScope(Drive::DRIVE);
$driveService = new Drive($client);
$fileMetadata = new Drive\DriveFile(array(
'name' => 'photo.jpg'));
$content = file_get_contents('../files/photo.jpg');
$file = $driveService->files->create($fileMetadata, array([
'data' => $content,
'mimeType' => 'image/jpeg',
'uploadType' => 'multipart',
'fields' => 'id']));
printf("File ID: %s\n", $file->id);
return $file->id;
} catch(Exception $e) {
echo "Error Message: ".$e;
}
I am generating PDF file using CodeIgniter and R&OS pdf class.
But now the problem is that the pdf is displayed to the browser.
I instead want it to be downloaded.
Here is my code :
$this->load->library('cezpdf');
$data['users'] = $this->user->get_all_ayant_droits();
foreach($data['users'] as $user) {
$db_data[] = array('name' => $user->nom, 'department' => $user->Department, 'status' => $user->Status);
}
$col_names = array(
'name' => 'Noms et Prenoms',
'department' => 'Département',
'status' => 'Status'
);
$this->cezpdf->ezTable($db_data, $col_names, 'Ayant droit Base Loisirs de Kribi', array('width'=>550));
$this->cezpdf->ezStream();
What is missing for this controller to send the file to download ?
You can pass the argument to the function ezStream
$this->cezpdf->ezStream(array $options);
$options 'compress' => 0/1 to enable compression. For compression level please use $this->options['compression'] = at the very first point. Default: 1
'download' => 0/1 to display inline (in browser) or as download. Default: 0
You can use Download Helper https://ellislab.com/codeigniter/user-guide/helpers/download_helper.html?
$this->load->helper('download');
$data = $this->cezpdf->ezStream();
force_download("PDF_filename.pdf", $data);
You can also use ouput variable by setting proper header value.
$this->output->set_header('Content-Disposition: attachment; filename="PDF_filename.pdf"');
$this->output->set_content_type('application/pdf')
->set_output($this->cezpdf->ezStream());
here by setting content type to appication/pdf so browser identify content is pdf and Content-Disposition: attachment force to download the file.
Hope this helps. Sorry for poor English.
I am using UploadedFile class in symfony2 to upload image files. I have allowed images with mime type image/jpg and image/png. When I upload a png file from my system to server its mime type is changed automatically. I know its changed because when I fire this command:
file --mime-type /home/mysuer/img.png
it gives me this:
home/myuser/img.png: image/png
but when I upload file and print UploadedFile object it gives following output:
Array
(
[media] => Symfony\Component\HttpFoundation\File\UploadedFile Object
(
[test:Symfony\Component\HttpFoundation\File\UploadedFile:private] =>
[originalName:Symfony\Component\HttpFoundation\File\UploadedFile:private] => img.png
[mimeType:Symfony\Component\HttpFoundation\File\UploadedFile:private] => application/octet-stream
[size:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 1246
[error:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 0
[pathName:SplFileInfo:private] => /tmp/phpivgnne
[fileName:SplFileInfo:private] => phpivgnne
)
)
Can any one suggest what's wrong?
EDIT
Here is my Code:
public function postFileAction(Request $request)
{
print_r($request->files->all());//Prints All files
$image = $request->files->get('media');
echo $image->getClientMimeType();exit; //Mime Type Information
if (empty($image)) {
throw new FileException('Invalid File, Please Select a Valid File.');
}
$uploader = $this->get('application.file_uploader');
if (!$this->container->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')) {
//authenticated (NON anonymous users)
throw new InvalidParameterException('Invalid User, Please use valid credentials');
}
return $uploader->upload($image, $this->get('security.context')->getToken()->getUser()->getId());
}
PHPUnit Test Code
public function testPostFile()
{
$file = tempnam(sys_get_temp_dir(), 'upl'); // create file
imagepng(imagecreatetruecolor(10, 10), $file); // create and write image/png to it
$image = new UploadedFile($file, 'new_image.png', 'image/png', 10, UPLOAD_ERR_OK);
$crawler = $this->client->request(
'POST', '/api/v1/files.json', array('submit' => 'Submit Form'), array('media' => $image)
);
$response = $this->client->getResponse();
$this->assertJsonResponse($response);
$this->assertContains('filename', $response->getContent());
print_r($response->getContent());exit;
$fileToken = json_decode($response->getContent());
$this->assertNotEmpty($fileToken->filename);
unlink($file);
unset($file, $image, $crawler, $response);
return $fileToken->filename;
}
I am using curl from command prompt to test my REST web service file upload like this:
curl -X POST myapi.com/app_dev.php/api/v1/files.json --form "media=#/home/myuser/img.png"
Finally I got the solutions to my problem will post the answer for problem in both for core-php and for symfony2 also.
Core PHP(found on http://php.net/manual/en/features.file-upload.php):
// DO NOT TRUST $_FILES['upfile']['mime'] VALUE !!
// Check MIME Type by yourself.
$finfo = new finfo(FILEINFO_MIME_TYPE);
if (false === $ext = array_search(
$finfo->file($_FILES['upfile']['tmp_name']),
array(
'jpg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
),
true
)) {
throw new RuntimeException('Invalid file format.');
}
So Never trust $_FILES['upfile']['mime'] always use fileinfo and this have given me the clue.
for Symfony2:
So I started looking into my code and found that I am using $file->getClientMimeType(), which is not good we have to use $file->getMimeType() which uses fileinfo to fetch mime type and will give the correct mime type.
In getMimeType() The mime type is guessed using a MimeTypeGuesser instance, which uses finfo(), mime_content_type() and the system binary "file" (in this order), depending on which of those are available.
Thanks Everyone for the Help :D
What i am trying to achieve:
Force download a zip file that contains user opted pdf files.
What i did in controller to achieve this:
Generate pdf reports in folder APP.WEBROOT_DIR.DS."package_files" (i used MPDF library)
*it generates correct readable pdf. I call here $this->render();
With Zip feature of php, Generate package.zip (which consists pdf files from above specified folder)
*it generates correct zip file, when downloaded from server it opens as valid zip file in windows.
Set the controller viewClass to Media and set parameters to force download as zip file,
*Again here I call here $this->render();
Issue:
When i run i get zip file but when opened with winrar, Zip file obtained reports Unexpected end of archive.
I am not getting any usefull articles to get through this issue...
What i guess is calling two times render is making file corrupt
Thanks
My controller code:
/** before this code i generate pdf files and have no issue **/
/** now scan through the directory and add all the pdf files to a zip archive **/
$dir = new Folder("".APP.WEBROOT_DIR.DS."package_files");
$files = $dir->find('.*\.pdf');
$zip = new ZipArchive();
foreach ($files as $file) {
$file_path = $dir->pwd() . DS . $file;
$filename = $dir->pwd() . DS ."package.zip";
if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
exit("cannot open <$filename>\n");
}
$zip->addFile($file_path,$file);
}
$zip->close();
/** now render the action to download the generated zip file **/
$this->viewClass = 'Media';
$params = array(
'id' => 'package.zip',
'name' => 'packaged_file',
'download' => true,
'extension' => 'zip',
'path' => APP . WEBROOT_DIR.DS.'package_files' . DS
);
$this->set($params);
$this->render();
at fisrt if you use Cakephp 2.3 use cake response file instaed of mediaView with these structure:
$this->response->file($file['path']);
// Return response object to prevent controller from trying to render
// a view
return $this->response;
here is doc: http://book.cakephp.org/2.0/en/controllers/request-response.html#cake-response-file
else remove $this->render(); at the end of your action and specify mime type option specially for zip and rar file for example for docx file add mime type option like:
// Render app/webroot/files/example.docx
$params = array(
'id' => 'example.docx',
'name' => 'example',
'extension' => 'docx',
'mimeType' => array(
'docx' => 'application/vnd.openxmlformats-officedocument' .
'.wordprocessingml.document'
),
'path' => 'files' . DS
);
I cannot understand how ->isuploaded() works. I am suppose to upload six images to display on my index page. Now the problem is, in my update function, if I upload only one or two image $upload->isUploaded() returns a false value, but if I decide to update all six of them it returns a true value. How do I deal with this problem? Am i missing out something here?
Here is my zend file transfer upload
$upload = new Zend_File_Transfer();
$upload->addValidator('Count', false, array('min' =>1, 'max' => 6))
->addValidator('Size', false, array('max' => '1Mb'))
->addValidator('ImageSize', false, array('minwidth' => 50,
'maxwidth' => 1000,
'minheight' => 50,
'maxheight' => 1000));
if ($upload->isUploaded()) $hasImage = true;
By default Zend guess all uploaded files are invalid even if just one of submitted form file fields was empty.
Zend docs are suggest to override this behavior by calling isValid() method before receive().
So I'm not sure if suggest best solution, but it works for me:
$upload = new Zend_File_Transfer();
$upload->setDestination( 'some your destination' );
if( $adapter->isValid( 'your form file field name' ) ){
$adapter->receive( 'your form file field name' );
}
And so on with every file field name. Wrap in foreach if needed.
Use isValid() instead.
if ($upload->isValid()) {
// success!
} else {
// failure!
}
Once you know your upload passed the validators, then start processing the images.