I've started a new project, creating Entities, Controller, CRUD.
I added field to upload images, when I create a new one, everything works fine but I'm struggling with edit and delete.
==============
/**
* #Route("/{id}/edit", name="post_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Post $post): Response
{
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var UploadedFile $imageFile */
$imageFile = $form->get('image')->getData();
if ($imageFile) {
$originalFilename = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$newFilename = $safeFilename.'-'.uniqid().'.'.$imageFile->guessExtension();
// Move the file to the directory
try {
$imageFile->move(
$this->getParameter('images_directory'),
$newFilename
);
} catch (FileException $e) {
echo 'Impossible d\'enregistrer l\'image';
}
$post->setImage($newFilename);
}
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('post_index');
}
return $this->render('post/edit.html.twig', [
'post' => $post,
'form' => $form->createView(),
]);
}
/**
* #Route("/{id}", name="post_delete", methods={"POST"})
*/
public function delete(Request $request, Post $post): Response
{
if ($this->isCsrfTokenValid('delete'.$post->getId(), $request->request->get('_token'))) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($post);
$entityManager->flush();
}
return $this->redirectToRoute('post_index');
}
==============
I would like to know how to remove/edit image file from images_directory.
==============
EDIT :
I've found this solution :
Post_edit:
/**
* #Route("/{id}/edit", name="post_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Post $post, LoggerInterface $logger): Response
{
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** #var UploadedFile $imageFile */
$imageFile = $form->get('image')->getData();
$imageFileName = $post->getImage();
if ($imageFile) {
$originalFilename = pathinfo($imageFile->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$newFilename = $safeFilename.'-'.uniqid().'.'.$imageFile->guessExtension();
// Move the file to the directory
try {
$imageFile->move(
$this->getParameter('images_directory'),
$newFilename
);
} catch (FileException $e) {
echo 'Impossible d\'enregistrer l\'image';
}
$pathToFile = $this->getParameter('images_directory').'/'.$imageFileName;
if (file_exists($pathToFile)) {
$logger->error("Le fichier $pathToFile existe.");
unlink($pathToFile);
} else {
$logger->error("Le fichier $pathToFile n'existe pas.");
}
$post->setImage($newFilename);
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($post);
$entityManager->flush();
return $this->redirectToRoute('post_index');
}
return $this->render('post/edit.html.twig', [
'post' => $post,
'form' => $form->createView(),
]);
}
==============
Post_delete :
/**
* #Route("/{id}", name="post_delete", methods={"POST"})
*/
public function delete(Request $request, Post $post, LoggerInterface $logger): Response
{
if ($this->isCsrfTokenValid('delete'.$post->getId(), $request->request->get('_token'))) {
$imageFileName = $post->getImage();
$pathToFile = $this->getParameter('images_directory').'/'.$imageFileName;
if (file_exists($pathToFile)) {
$logger->error("Le fichier $pathToFile existe.");
unlink($pathToFile);
} else {
$logger->error("Le fichier $pathToFile n'existe pas.");
}
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($post);
$entityManager->flush();
}
return $this->redirectToRoute('post_index');
}
CAUTION: you are storing user data as directory/file, this is dangerous and strongly discouraged, because the user can alter the POST data and overwrite/delete/read stuff from other users and your filesystem. This is a BIG security exploit. You should determine the filepath yourself, not let the user browse through your directories.
That being said, if you still want to proceed using this approach:
You are not storing the filepath, so you can never know where the file was stored. Store the filepath together with the filename in your post, like this:
//...
// Move the file to the directory
try {
$pathToFile = $this->getParameter('images_directory').'/'.newFilename;
$imageFile->move(
$this->getParameter('images_directory'),
$newFilename
);
} catch (FileException $e) {
echo 'Impossible d\'enregistrer l\'image';
}
$post->setImage($pathToFile);
//...
Related
I created a function so a user can upload files for an article/finished project. These uploaded images will appear on the homepage of the website.
When I am trying to upload files to a specific folder in my development environment, it works. However if I try to upload files in the production enviroment, the files won't move to the destination folder. Is there anyone that had a similar problem/can help me out?
The code down below is the Entity called Artikel (Article in English) Afbeeldingen -> Images in English
/**
* #ORM\Entity(repositoryClass="App\Repository\ArtikelRepository")
*/
class Artikel
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="array")
*/
private $afbeeldingen = [];
public function getAfbeeldingen()
{
return $this->afbeeldingen;
}
public function setAfbeeldingen(array $afbeeldingen): self
{
$this->afbeeldingen = $afbeeldingen;
return $this;
}
}
The code down below is the services.yaml file in which the upload directory is configured (images_directory)
parameters:
locale: 'en'
images_directory: '%kernel.project_dir%/www/uploads/artikelen'
The code down below is the Controller for the Artikel entity called ArtikelController (Article and ArticleController in English)
/**
* #Route("/admin/artikel/new", name="artikel_new", methods={"GET","POST"})
*/
public function new(Request $request): Response
{
$artikel = new Artikel();
$form = $this->createForm(ArtikelType::class, $artikel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$articleImages = $form->get('afbeeldingen')->getData();
$images_directory = $this->getParameter('images_directory');
$afbeeldingen = array();
$teller = 0;
foreach ($articleImages as $articleImage) {
$originalFilename = pathinfo($articleImage->getClientOriginalName(), PATHINFO_FILENAME);
$filename = Urlizer::urlize($originalFilename) . '-' . uniqid() . '.' . $articleImage->guessExtension();
$afbeeldingen[$teller] = $filename;
$teller += 1;
try {
$articleImage->move($images_directory, $filename);
} catch (FileException $e) {
}
}
$artikel->setAfbeeldingen($afbeeldingen);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($artikel);
$entityManager->flush();
$this->addFlash('success', 'Artikel is aangemaakt.');
return $this->redirectToRoute('artikel_index');
}
return $this->render('artikel/new.html.twig', [
'artikel' => $artikel,
'form' => $form->createView(),
]);
}
I would be glad if someone could help me out! Thanks in advance!
the "www" folder is outside of the "public" folder.
You cannot access any folder outside the public.
set the service.yaml like that and try again and double-check the file system permission.
'%kernel.project_dir%/public/uploads/artikelen'
Im saving a request to my database from my vue js via;
public function store(Request $request)
{
//validate
$this->validate($request, [
'name' => 'required',
'description' => 'required',
'price' => 'required'
]);
//get image
$exploded = explode(',', $request->cover_image);
$decoded = base64_decode($exploded[1]);
if(str_contains($exploded[0],'jpeg'))
$extension = 'jpg';
else
$extension = 'png';
$fileName = str_random().'.'.$extension;
$path = public_path().'/cover_images/'.$fileName;
file_put_contents($path, $decoded);
//save
$product = new Product;
$product->name = $request->input('name');
$product->description = $request->input('description');
$product->price = $request->input('price');
$product->cover_image = $fileName;
if($product->save()) {
return new ProductsResource($product);
}
}
How can I validate the base64 image? Is my procedure in saving the image coming from vue js is in correct way or is there a better way? please let me know. Thanks im just new to laravel and vue js hoping to learn more
You should add this function to your custom helper :
if (!function_exists('validate_base64')) {
/**
* Validate a base64 content.
*
* #param string $base64data
* #param array $allowedMime example ['png', 'jpg', 'jpeg']
* #return bool
*/
function validate_base64($base64data, array $allowedMime)
{
// strip out data uri scheme information (see RFC 2397)
if (strpos($base64data, ';base64') !== false) {
list(, $base64data) = explode(';', $base64data);
list(, $base64data) = explode(',', $base64data);
}
// strict mode filters for non-base64 alphabet characters
if (base64_decode($base64data, true) === false) {
return false;
}
// decoding and then reeconding should not change the data
if (base64_encode(base64_decode($base64data)) !== $base64data) {
return false;
}
$binaryData = base64_decode($base64data);
// temporarily store the decoded data on the filesystem to be able to pass it to the fileAdder
$tmpFile = tempnam(sys_get_temp_dir(), 'medialibrary');
file_put_contents($tmpFile, $binaryData);
// guard Against Invalid MimeType
$allowedMime = array_flatten($allowedMime);
// no allowedMimeTypes, then any type would be ok
if (empty($allowedMime)) {
return true;
}
// Check the MimeTypes
$validation = Illuminate\Support\Facades\Validator::make(
['file' => new Illuminate\Http\File($tmpFile)],
['file' => 'mimes:' . implode(',', $allowedMime)]
);
return !$validation->fails();
}
}
Then extend the base64_image validation in your AppServiceProvider in boot() method :
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
...
Validator::extend('base64_image', function ($attribute, $value, $parameters, $validator) {
return validate_base64($value, ['png', 'jpg', 'jpeg', 'gif']);
});
}
Now you can use it in your validation rules like this :
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'photo' => 'required|base64_image'
];
}
There is a crazybooot/base64-validation package that handles base64 validation.
For installation instructions and more details see:
https://github.com/crazybooot/base64-validation
I want to click on an image opens the window for selecting a new image, and change the screen, now I want to change this picture in the bank, but this pending on an error:
Catchable Fatal Error: Argument 1 passed to
Delivve\WebBundle\Entity\User::setFile() must be an instance of
Symfony\Component\HttpFoundation\File\UploadedFile, string given,
called in
/home/delivve-webservice/src/Delivve/WebBundle/Controller/UserController.php
I do not know whether the error is in the path image that I'm taking?
$("#bundle_user_file").change(function () {
if (this.files && this.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('.active-img').attr('src', e.target.result);
ajax_formData(e.target.result);
};
reader.readAsDataURL(this.files[0]);
}
});
function ajax_formData(image) {
var path = "{{ path("submit_image_user", {"userId" : owner.id}) }}";
alert(image);
$.post(path, {image: image}, function (data) {
alert(data.message);
}, "json");
}
public function submitImageAction(Request $request, $userId){
$this->denyAccessUnlessGranted('ROLE_USER', null, 'Unable to access this page!');
$em = $this->getDoctrine()->getManager();
$entity = $this->getUser();
if ($entity->getId() != $userId) {
$response = new JsonResponse(
array(
'message' => "Não tem permissão de alterar esses dados"
), 400);
return $response;
}
if ($base64Content = $request->request->get('image')) {
$filePath = tempnam(sys_get_temp_dir(), 'UploadedFile');
$file = fopen($filePath, "w");
stream_filter_append($file, 'convert.base64-decode');
fwrite($file, $base64Content);
$meta_data = stream_get_meta_data($file);
$path = $meta_data['uri'];
fclose($file);
$entity->setFile($path);
$entity->upload();
$em->persist($entity);
$em->flush();
return new JsonResponse(array('message' => 'Success!'), 200);
}
$response = new JsonResponse(
array(
'message' => "imagem não encontrada"
), 400);
return $response;
}
these are the methods in my class
user.php
/**
* #return string
*/
public function getPictureUrl()
{
return $this->pictureUrl;
}
/**
* #param string $pictureUrl
*/
public function setPictureUrl($pictureUrl)
{
$this->pictureUrl = $pictureUrl;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Set file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Relative path.
* Get web path to upload directory.
*
* #return string
*/
public function getUploadPath()
{
return 'uploads/pictures';
}
/**
* Absolute path.
* Get absolute path to upload directory.
*
* #return string
*/
protected function getUploadAbsolutePath()
{
return __DIR__ . '/../../../../web/' . $this->getUploadPath();
}
/**
* Relative path.
* Get web path to a cover.
*
* #return null|string
*/
public function getPictureWeb()
{
return null === $this->getPictureUrl()
? null
: $this->getUploadPath() . '/' . $this->getPictureUrl();
}
/**
* Get path on disk to a cover.
*
* #return null|string
* Absolute path.
*/
public function getPictureAbsolute()
{
return null === $this->getPictureUrl()
? null
: $this->getUploadAbsolutePath() . '/' . $this->getPictureUrl();
}
/**
* Upload a cover file.
*/
public function upload()
{
if (null === $this->getFile()) {
return;
}
$filename = $this->getFile()->getClientOriginalName();
$this->getFile()->move($this->getUploadAbsolutePath(), $filename);
$this->setPictureUrl($filename);
$this->setFile();
}
the file I get comes from the type base64, I have to turn it into a uploadfile
$file = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAA..."
I would greatly appreciate it if someone could help me to do this submition of asynchronous file.
public function submitImageAction(Request $request, $userId)
{
$this->denyAccessUnlessGranted('ROLE_USER', null, 'Unable to access this page!');
$em = $this->getDoctrine()->getManager();
$entity = $this->getUser();
if ($entity->getId() != $userId) {
$response = new JsonResponse(
array(
'message' => "Não tem permissão de alterar esses dados"
), 400);
return $response;
}
if ($base64Content = $request->request->get('image')) {
$dat = preg_split("/,/", $base64Content);
if (($fileData = base64_decode($dat[1])) === false) {
$response = new JsonResponse(
array(
'message' => "Base64 decoding error."
), 400);
return $response;
}
$fileName = $entity->getUploadPath() . "/" . uniqid() . ".jpeg";
if (file_put_contents($fileName, $fileData)) {
$entity->setPictureUrl($fileName);
$em->persist($entity);
$em->flush();
return new JsonResponse(array('message' => 'Success!'), 200);
}
}
$response = new JsonResponse(
array(
'message' => "imagem não encontrada"
), 400);
return $response;
}
the error was in the driver only need to hit like sending the path to the User
The action included at the end of this post is called from an ajax call via jQuery. When I try to delete more than one record, just the first is deleted but the flash messages are printed for every request.
I checked the input data and it is correct. No exception are throwed.
Any idea? Thanks!
/**
* #Route("/cancella", name="training_plan_activity_edition_final_delete")
* #Method({"POST"})
*
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Request $request)
{
if (!$request->isXmlHttpRequest()) {
throw new \Exception('this controller allows only ajax requests');
}
$ids = json_decode($request->get('ids'), true);
$em = $this->getDoctrine()->getManager();
foreach ($ids as $id) {
if($id <> 'on') {
$trainingPlanActionEditionFinal = $this->getDoctrine()
->getRepository('TrainingPlanBundle:TrainingPlanActionEditionFinal')
->find($id);
if (!$trainingPlanActionEditionFinal) {
throw $this->createNotFoundException(
'Training plan action edition final with id ' . $id . ' not found'
);
}
$trainingPlanSlug = $trainingPlanActionEditionFinal->getSlug();
try {
$em->remove($trainingPlanActionEditionFinal);
$em->flush();
$this->get('session')->getFlashbag()->add('success', 'Cancellazione dell\'edizione avvenuta con successo');
} catch (\Exception $e) {
$this->get('session')->getFlashbag()->add('error', 'Impossibile cancellare l\'edizione');
}
}
}
return new Response($this->generateUrl('training_plan_action_edition_final_list', array(
'trainingPlanSlug' => $trainingPlanSlug
)
));
}
I'm a new user of Google Cloud Storage, so bear with me.
I'm trying to create a file editor, that gets a non-binary file from GCS, and saves it back.
I'm using google-api-php-client. I've been experimenting a lot using the API, browsed, but I just couldn't find the proper answer.
<?php
class GCS_Driver {
/** #var Google_Service_Storage $driver */
public $driver;
/** #var string $bucket */
public $bucket;
public function updateObject($objectPath,$content) {
$updated = false;
try {
$postBody = new Google_Service_Storage_StorageObject();
$updated = $this->driver->objects->patch($this->bucket,$objectPath,$postBody,array(
'data' => $content // I know this is wrong, I am just showing the idea I am looking for to overwrite the content
));
} catch (Exception $ex) {
// error log
return false;
}
if (!$updated) {
// error log
return false;
}
return true;
}
}
Any hint will be appreciated.
I figured out the work around.
Basically, just save a temp file, and upload it to overwrite the existing
Here's the code.
<?php
class GCS_Driver {
/** #var Google_Client $client */
public $client;
/** #var Google_Service_Storage $driver */
public $driver;
/** #var string $bucket */
public $bucket;
public function updateObject($objectPath,$content) {
$updated = false;
try {
$temp = tempnam(sys_get_temp_dir(), 'gcs');
$handle = fopen($temp, "r+b");
fwrite($handle, $content);
fseek($handle, 0);
$postBody = new Google_Service_Storage_StorageObject();
$postBody->setName($objectPath);
$postBody->setUpdated(date("c"));
$postBody->setGeneration(time());
$size = #filesize($temp);
$postBody->setSize($size);
$ext = #pathinfo($objectPath,PATHINFO_EXTENSION);
$ext = strtolower($ext);
$mimeType = $this->getContentType($ext);
$postBody->setContentType($mimeType);
$chunkSizeBytes = 1 * 1024 * 1024;
$this->client->setDefer(true);
$request = $this->driver->objects->insert($this->bucket,$postBody,array(
'name' => $objectPath,
'predefinedAcl' => 'publicRead',
'projection' => 'full',
));
$media = new Google_Http_MediaFileUpload(
$this->client,
$request,
$mimeType,
null,
true,
$chunkSizeBytes
);
$media->setFileSize($size);
$status = false;
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
unlink($temp);
if ($status) {
$updated = true;
}
} catch (Exception $ex) {
// error log
return false;
}
if (!$updated) {
// error log
return false;
}
// action log
return true;
}
}
Any new feedback is welcome.