I'm trying to setup jQuery-File-Upload UI with oneupuploaderBundle on a symfony2 Website.
After a lot of tears, the upload process works as I want,but I can't make the Json response work avec the upload.
I use the PostUploadEvent of the oneupuploaderBundle, And tied a lot of solution to send a corecte jSon response to jQuery-File-Upload UI, but i still have an error message when the file has been uploaded.
In the doc, the ask to send a resonse like this:
{"files": [
{
"name": "picture1.jpg",
"size": 902604,
"url": "http:\/\/example.org\/files\/picture1.jpg",
"thumbnail_url": "http:\/\/example.org\/files\/thumbnail\/picture1.jpg",
"delete_url": "http:\/\/example.org\/files\/picture1.jpg",
"delete_type": "DELETE"
}
]}
Here is mine :
{"files": [
{
"url": "\/ishowpro-cms\/web\/app_dev.php\/library\/file\/image\/originals\/51dbd9a13a065-screen-shot-mini.png",
"thumbnail_url": "\/ishowpro-cms\/web\/app_dev.php\/library\/file\/image\/thumbnails\/51dbd9a13a065-screen-shot-mini.png",
"name": "screen-shot-mini.png",
"type": "image/png",
"size": 1192,
"delete_url": "http://nourltodelete.com",
delete_type: "DELETE"
}
] }
The URls are OK.
I tried to return the response with
return new JsonResponse($jsonResponse);
with the Normal response object and json headers, I tried to create an entity and serialize it :
$responseObjectFile = new JqUploaderFile();
$responseObjectFile->setName();
$responseObjectFile->setSize();
$responseObjectFile->setUrl();
$responseObjectFile->setThumbnail_url();
$responseObjectFile->setDelete_url($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true));
$responseObjectFile->setDelete_type("DELETE");
//create serializer to encode Entity
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new GetSetMethodNormalizer());
$serializer = new Serializer($normalizers, $encoders);
$jsonContent = $serializer->serialize($responseObjectFile, 'json');
//add entity in array and unserilize it....
$responseCode = array('files'=>array($jsonContent) );
And also to use the $event->getRequest(); of the object.
But nothing works.
Is their someone who can help me please ?
Thank you in advance.
Here is the complete file
<?php
namespace Reyner\Ishowpro\LibraryBundle\Upload;
use Oneup\UploaderBundle\Event\PostUploadEvent;
use Oneup\UploaderBundle\Event\PreUploadEvent;
use Reyner\Ishowpro\LibraryBundle\Tools\Tools;
use Reyner\Ishowpro\LibraryBundle\Entity\Folder;
use Reyner\Ishowpro\LibraryBundle\Entity\File;
use Reyner\Ishowpro\LibraryBundle\Entity\JqUploaderFile;
use Symfony\Component\HttpFoundation\File\File as sfFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
use Gaufrette\File as GaufretteFile;
class UploadListener
{
private $doctrine;
private $logger;
private $router;
public function __construct($doctrine, $logger, \Symfony\Bundle\FrameworkBundle\Routing\Router $router)
{
$this->doctrine = $doctrine;
$this->logger = $logger;
$this->router = $router;
}
public function onPreUpload(PreUploadEvent $event)
{
$file = $event->getFile();
}
public function onUpload(PostUploadEvent $event)
{
$this->logger->info('----------------------------------------------------');
$this->logger->info('------------------ service begin ----------------------');
$request = $event->getRequest();
//$requestFile = $request->files->all();
//$this->logger->info($requestFile["files"][0]->getClientOriginalName());
$em = $this->doctrine->getManager();
$tempfile = $event->getFile();
$parameters = $request->request->all();
/* getFile: Get the uploaded file. Is either an instance of Gaufrette\File or Symfony\Component\HttpFoundation\File\File.
getRequest: Get the current request including custom variables.
getResponse: Get the response object to add custom return data.
getType: Get the name of the mapping of the current upload. Useful if you have multiple mappings and EventListeners.
getConfig: Get the config of the mapping.
*/
$parameters = $request->query->all();
//stock ids to add "new" badges
$newIds = array();
$folder = $em->getRepository('LibraryBundle:Folder')->find($request->getSession()->get('currentFolder'));
$library = $em->getRepository('LibraryBundle:Library')->find($request->getSession()->get('libraryId'));
// $this->logger->info('folder: '.$folder->getId());
//persit in BDD
$file = new File();
$file->setLibrary($library);
$file->setFolder($folder);
$file->setSize($tempfile->getSize());
$filenamePart = explode("--", $tempfile->getName());
$pathinfo = pathinfo($tempfile->getName());
$file->setExtension(strtolower($pathinfo["extension"]));
$file->setName($pathinfo['filename']);
//check mime type and correct it if docx, xlsx or pptx
if(Tools::getMymeTypeFromExt($pathinfo["extension"])){
$file->setMimetype(Tools::getMymeTypeFromExt($pathinfo["extension"]));
}else{
$fi = new \finfo(FILEINFO_MIME_TYPE);
$file->setMimetype($fi->buffer($tempfile->getContent()));
}
$file->setFilename($filenamePart[1]);
$em->persist($file);
$em->flush();
//create thumb
if($file->isImage()){
$this->logger->info('is an Image');
$imagepath = $file->getLocalFileDirectory().$tempfile->getName();
$this->logger->info('image path: '.$imagepath);
$file->encodeAndMoveFile($imagepath);
$this->logger->info('----------------------------------------------------');
}else{
$this->logger->info('is not an Image');
$this->logger->info($file->getLocalFileDirectory().$tempfile->getName());
$file->moveTempFile($file->getLocalFileDirectory().$tempfile->getName());
}
//add id to the New session array
if($request->getSession()->get("newfilesids")){
$newIds = unserialize($request->getSession()->get("newfilesids"));
}
$newIds[] = $file->getId();
$request->getSession()->set("newfilesids", serialize($newIds));
$this->logger->info('JqUploaderFile');
$this->logger->info('----------------------------------------------------');
/********************************************
*
* BUILD RESPONSE
*
******************************************/
$this->logger->info('BUILD RESPONSE');
/*//test1
//create object needed to JqUploader response
$responseObjectFile = new JqUploaderFile();
$responseObjectFile->setName($file->getFileName());
$responseObjectFile->setSize($file->getSize());
$responseObjectFile->setUrl($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true));
$responseObjectFile->setThumbnail_url($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'thumbnails'), true));
$responseObjectFile->setDelete_url($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true));
$responseObjectFile->setDelete_type("DELETE");
$this->logger->info('-------------------------------------------------------');
$this->logger->info('ENTITY READY');
*/
//test 2
$response = $event->getResponse();
$response["file"][]['name'] = $file->getFileName();
$response["file"][]['size'] = $file->getSize();
$response["file"][]['url'] = $this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true);
$response["file"][]['thumbnail_url'] = $this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'thumbnails'), true);
$response["file"][]['delete_url'] = $this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true);
$response["file"][]['delete_type'] = "DELETE";
return $response;
//test 3
/*
$jsonResponse = '{"files": [ { "url": '.json_encode($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'originals'), true)).', "thumbnail_url": '.json_encode($this->router->generate('show_image_link', array('slug'=>$file->getSlug(), 'extension'=>$file->getExtension(), 'size'=>'thumbnails'), true)).', "name": '.json_encode($file->getFileName()).', "type": "'.$file->getMimeType().'", "size": '.$file->getSize().', "delete_url": "http://nourltodelete.com", "delete_type": "DELETE" } ] }';
return $jsonResponse;
*/
}
}
Thanks to devsheeep, here is the solution.
Really simple when you know it :
$response = $event->getResponse();
$files = array();
$files[0] = array(
'name' => $file->getFileName(),
'size' => $file->getSize(),
'url' => "Your URL",
'thumbnail_url' => "Your thumb URL",
'delete_url' =>"Your delete URL",
'delete_type' => 'DELETE'
);
$response['files'] = $files;
And we are in an Event context, return values have no effects. Instead we are working with references. So just omit the return statement.
5 to create it, I love.
Thanks again to desheep and the 1up.io team, they are great professionals of the Symfony Framework.
Related
I am using Google APIs Client Library for PHP. I can create and access the DeveloperMetadata of the spreadsheet. But cannot update the DeveloperMetadata. Please see the below code for a better understanding.
<?php
class GoogleSheetHelper {
function update_dev_meta() {
$dev_meta = new DeveloperMetadata();
$dev_meta->setVisibility( 'DOCUMENT' );
$dev_meta->setMetadataKey( 'new_meta_key' );
$dev_meta->setMetadataValue( 'new_meta_value_updated' );
$update_req = new UpdateDeveloperMetadataRequest();
$update_req->setDeveloperMetadata( $dev_meta );
$update_req->setFields( '*' );
$request = new Request();
$request->setUpdateDeveloperMetadata( $update_req );
$batch_update = new BatchUpdateSpreadsheetRequest();
$batch_update->setRequests( [ $request ] );
$client = $this->get_client();
$service = new Sheets( $client );
try {
return $service->spreadsheets->batchUpdate( $spreadsheet_id, $batch_update );
} catch ( Exception $ex ) {
var_dump( $ex->getErrors() );
return false;
}
}
private function get_client( $scopes = [] ) {
$credential = 'location/of/credential.json';
if ( empty( $credential ) ) {
return null;
}
$default_scopes = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/spreadsheets',
];
return new Client(
[
'credentials' => $credential,
'scopes' => $default_scopes,
]
);
}
}
Feel free to notify me of any queries. Sorry for my poor English skill.
Checking your code and the documentation it seems that you are missing to specify a Data Filter and a DeveloperMetadata Object including a field mask that declares fields to be updated. Please check the documentation on how to update the developer metadata to corroborate you are doing it properly.
Here is the example shown that updates the developer metadata, it's not that far from what you currently have:
{
"requests": [
{
"updateDeveloperMetadata": {
"dataFilters": [
{
"developerMetadataLookup": {
"metadataId": metadataId
}
}
],
"developerMetadata": {
"location": {
"sheetId": sheetId
},
"metadataKey": "SalesUpdated"
},
"fields": "location,metadataKey"
}
}
]
}
I am brand new to PSR standards, and I am not sure if I adapted my code to PSR-7, PSR-15 correctly.
My code is handling a POST request to delete a group of products by receiving an array of ids.
Is that a correct adaptation? Thanks.
<?php
require_once 'DataBase.php';
require_once 'config.php';
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class DeleteRequest implements RequestHandlerInterface
{
private $DB;
public function __construct(DataBase $DB)
{
$this->DB = $DB;
}
//Delete each product from the database using the ID
public function handle(ServerRequestInterface $request): ResponseInterface
{
// Make sure it is a POST request
if ($request->getMethod() !== 'POST') {
throw new Exception('Incorrect REQUEST_METHOD. '.
'Only POST requests are allowed.');
}
// Extract the 'ids' array from the request data
MyLogV($request->getBody()->getContents());
$data = json_decode($request->getBody()->getContents(), true);
// Make sure the 'ids' array is present in the data
if (!isset($data['ids'])) {
throw new Exception('Missing required parameter: ids');
}
$ids = $data['ids'];
foreach ($ids as $id) {
myLog("DeleteRequest->handle","id",$id);
$result = $this->DB->deleteProduct($id);
if ($result['status'] != 'success') break;
}
// Generate the response: 200 => OK, 400 => Bad request
$status = $result['status'] == 'success' ? 200 : 400;
$response = new JsonResponse($result, $status);
myLogV($result['status']);
return $response;
}
}
try {
$serverRequest = ServerRequestFactory::fromGlobals();
$DB = new DataBase();
$deleteRequest = new DeleteRequest($DB);
$response = $deleteRequest->handle($serverRequest);
$response->send();
} catch (Exception $e) {
myLog("delete.php","Exception",$e->getMessage());
$result = ['status' => 'error','message'=> $e->getMessage()];
$response = new JsonResponse($result, 400);
$response->send();
}
exit();
?>
I tried to understand the PSR standards.
I have the following line of code
$apps = $appRepositry->findBy(['user' => $user]);
I then return the results via JSON using the following
$json_data[] = array(
"state" => "success",
"user_apps" => $apps,
"total_registerd_apps" => count($apps),
);
The issue I have is that the count is correct but the "user_apps" array is returning blank.
{
state = success;
"total_registerd_apps" = 1;
"user_apps" = (
{
}
);
}
I think it is because the FindBy returns objects but I am not sure. Could someone explain how to convert the result to an array that can then be sent via JSON?
Thanks
You need to use a serializer with groups.
Information about serialization :
http://symfony.com/doc/current/components/serializer.html
Information about serialization groups :
https://symfony.com/blog/new-in-symfony-2-7-serialization-groups
public function listAction(ParamFetcherInterface $paramFetcher, Request $request)
{
//...your code...//
$yourObjectifiedResult = $appRepositry->findBy(['user' => $user]);
$yourNormalizedResult = $this->normalizeWithCircularHandler(
$yourObjectifiedResult,
['groups']);
//Serializer will convert your result from Object to Json Array automatically. To get a specific set of data, you can use groups.
}
public function normalizeWithCircularHandler($data, $displayGroups, $normalizerCallbacks = false)
{
$encoder = new JsonEncoder();
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setCircularReferenceHandler(function ($object) {
return $object->getId();
});
if(false != $normalizerCallbacks && is_array($normalizerCallbacks)) {
$normalizer->setCallbacks($normalizerCallbacks);
}
$serializer = new Serializer([$normalizer], [$encoder]);
return $serializer->normalize($data , null, ['groups' => $displayGroups]);
}
You need to use a serializer to serialize your data into JSON:
https://symfony.com/doc/current/serializer.html
Im trying to test an upload API but it fails every time:
Test Code :
$JSONResponse = $this->call('POST', '/upload', [], [], [
'photo' => new UploadedFile(base_path('public/uploads/test') . '/34610974.jpg', '34610974.jpg')
]);
$this->assertResponseOk();
$this->seeJsonStructure(['name']);
$response = json_decode($JSONResponse);
$this->assertTrue(file_exists(base_path('public/uploads') . '/' . $response['name']));
file path is /public/uploads/test/34610974.jpg
Here is My Upload code in a controller :
$this->validate($request, [
'photo' => 'bail|required|image|max:1024'
]);
$name = 'adummyname' . '.' . $request->file('photo')->getClientOriginalExtension();
$request->file('photo')->move('/uploads', $name);
return response()->json(['name' => $name]);
How should I test file upload in Laravel 5.2? How to use call method to upload a file?
When you create an instance of UploadedFile set the last parameter $test to true.
$file = new UploadedFile($path, $name, filesize($path), 'image/png', null, true);
^^^^
Here is a quick example of a working test. It expects that you have a stub test.png file in tests/stubs folder.
class UploadTest extends TestCase
{
public function test_upload_works()
{
$stub = __DIR__.'/stubs/test.png';
$name = str_random(8).'.png';
$path = sys_get_temp_dir().'/'.$name;
copy($stub, $path);
$file = new UploadedFile($path, $name, filesize($path), 'image/png', null, true);
$response = $this->call('POST', '/upload', [], [], ['photo' => $file], ['Accept' => 'application/json']);
$this->assertResponseOk();
$content = json_decode($response->getContent());
$this->assertObjectHasAttribute('name', $content);
$uploaded = 'uploads'.DIRECTORY_SEPARATOR.$content->name;
$this->assertFileExists(public_path($uploaded));
#unlink($uploaded);
}
}
➔ phpunit tests/UploadTest.php
PHPUnit 4.8.24 by Sebastian Bergmann and contributors.
.
Time: 2.97 seconds, Memory: 14.00Mb
OK (1 test, 3 assertions)
In Laravel 5.4 you can also use \Illuminate\Http\UploadedFile::fake(). A simple example below:
/**
* #test
*/
public function it_should_allow_to_upload_an_image_attachment()
{
$this->post(
action('AttachmentController#store'),
['file' => UploadedFile::fake()->image('file.png', 600, 600)]
);
/** #var \App\Attachment $attachment */
$this->assertNotNull($attachment = Attachment::query()->first());
$this->assertFileExists($attachment->path());
#unlink($attachment->path());
}
If you want to fake a different file type you can use
UploadedFile::fake()->create($name, $kilobytes = 0)
More information directly on Laravel Documentation.
I think this is the easiest way to do it
$file=UploadedFile::fake()->image('file.png', 600, 600)];
$this->post(route("user.store"),["file" =>$file));
$user= User::first();
//check file exists in the directory
Storage::disk("local")->assertExists($user->file);
and I think the best way to delete uploaded files in the test is by using tearDownAfterClass static method,
this will delete all uploaded files
use Illuminate\Filesystem\Filesystem;
public static function tearDownAfterClass():void{
$file=new Filesystem;
$file->cleanDirectory("storage/app/public/images");
}
The laravel documentation has an answer for when you want to test a fake file. When you want to test using a real file in laravel 6 you can do the following:
namespace Tests\Feature;
use Illuminate\Http\UploadedFile;
use Tests\TestCase;
class UploadsTest extends TestCase
{
// This authenticates a user, useful for authenticated routes
public function setUp(): void
{
parent::setUp();
$user = User::first();
$this->actingAs($user);
}
public function testUploadFile()
{
$name = 'file.xlsx';
$path = 'absolute_directory_of_file/' . $name;
$file = new UploadedFile($path, $name, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', null, true);
$route = 'route_for_upload';
// Params contains any post parameters
$params = [];
$response = $this->call('POST', $route, $params, [], ['upload' => $file]);
$response->assertStatus(200);
}
}
You can find this code at this link
Setup
/**
* #param $fileName
* #param $stubDirPath
* #param null $mimeType
* #param null $size
*
* #return \Illuminate\Http\UploadedFile
*/
public static function getTestingFile($fileName, $stubDirPath, $mimeType = null, $size = null)
{
$file = $stubDirPath . $fileName;
return new \Illuminate\Http\UploadedFile\UploadedFile($file, $fileName, $mimeType, $size, $error = null, $testMode = true);
}
Usage
$fileName = 'orders.csv';
$filePath = __DIR__ . '/Stubs/';
$file = $this->getTestingFile($fileName, $filePath, 'text/csv', 2100);
Folder Structure:
- MyTests
- TestA.php
- Stubs
- orders.csv
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.