Symfony 4 Weird thing when uploading file in a REST Api? - api

I have a really simple controller (with FOSRESTBundle)
/**
* #Rest\Post("/posts/{id}/attachments", name="api_create_attachment", requirements={"pId"="\d+"})
* #Rest\View()
*/
public function createAttachments(Request $request)
{
/** #var UploadedFile $attachment */
$attachment = $request->files->get('file');
return $attachment->getFilename();
}
I just want to dump the filename from POST request of /api/posts/{id}/attachments. I use Postman with the Content-Type: multipart/form-data header for allowing the file to be send.
Now, i have two files (pp.jpg: 163kb and para.jpg: 358kb). When i try to send the request with pp.jpg it's working perfectly and it return "phpyl3yNE" (the filename). But now, when i'm trying with para.jpg which is not really different, it return me an error "Call to a member function getFilename() on null". And it's the same with many differents files ! I can't figure out why ... i use the symfony serve command for the server and from phpinfo, post_max_size is set to 8M and upload_max_filesize is set to 2M.
I'm stuck at this point .. I can't figure out why it return me null ...
Thank you, have a nice day !

Related

symfony 4 Upload

How to upload a file in symfony 4.I have done with the symfony document. I don't know where I have missed something. Its throws error while uploading file give me some clues
REFERED LINK:
https://symfony.com/doc/current/controller/upload_file.html
ERROR:
The file "" does not exist
Entity
public function getBrochure()
{
return $this->brochure;
}
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
File upload Listener
class FileUploader
{
private $targetDirectory;
public function __construct($targetDirectory)
{
$this->targetDirectory = $targetDirectory;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDirectory(), $fileName);
return $fileName;
}
public function getTargetDirectory()
{
return $this->targetDirectory;
}
}
This Symfony tutorial works fine for me so I'll try to explain how and perhaps it will help you or people still looking for an answer, this post getting a bit old.
So first you have to create the FileUploader service in App\Service for better reusability (chapter: Creating an Uploader Service). You can basically copy/paste what they've done here, it works like a charm. Then you need to open your services.yaml in Config folder and explicit your brochure directory:
parameters:
brochures_directory: '%kernel.project_dir%/public/uploads/brochures'
# ...
services:
# ...
App\Service\FileUploader:
arguments:
$targetDirectory: '%brochures_directory%'
Now everything is normally ready to use your FileUploader service.
So if you're in your controller (for example), I guess you want to use it in a form. Thus, you just have to do this (don't forget to use your Service in your Controller):
public function myController(FileUploader $fileUploader)
{
// Create your form and handle it
if ($form isValid() && &form isSubmitted()) {
$file = $myEntity->getBrochure();
$fileName = $this->fileUploader->upload($file);
$myEntity->setBrochure($fileName);
// Form validation and redirection
}
// Render your template
}
One important point I forgot to say. In your FormType, you need to say that the Brochure will be a FileType:
$builder->add('brochure', FileType::class)
But in your entity you have to specify your brochure is stored as a "string":
/**
* #MongoDB\Field(type="string")
*/
protected $brochure;
The reason is your file is getting uploaded and saved in your public/uploads/brochure. But your database is only remembering a string path to reach it.
I hope this will help!

Understanding seam filter url-pattern and possible conflicts

I made a custom editor plugin, in a Seam 2.2.2 project, which makes file upload this way:
1) config the editor to load my specific xhtml upload page;
2) call the following method inside this page, and return a javascript callback;
public String sendImageToServer()
{
HttpServletRequest request = ServletContexts.instance().getRequest();
try
{
List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
processItems(items);//set the file data to specific att
saveOpenAttachment();//save the file to disk
}
//build callback
For this to work I have to put this inside components.xml:
<web:multipart-filter create-temp-files="false"
max-request-size="1024000" url-pattern="*"/>
The attribute create-temp-files do not seems to matter whatever its value.
But url-pattern has to be "" or "/myUploadPage.seam", any other value makes the item list returns empty. Does Anyone know why?
This turns into a problem because when I use a url-pattern that work to this case, every form with enctype="multipart/form-data" in my application stops to submit data. So I end up with other parts of the system crashing.
Could someone help me?
To solve my problem, I changed the solution to be like Seam multipart filter handle requests:
ServletRequest request = (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
try
{
if (!(request instanceof MultipartRequest))
{
request = unwrapMultipartRequest(request);
}
if (request instanceof MultipartRequest)
{
MultipartRequest multipartRequest = (MultipartRequest) request;
String clientId = "upload";
setFileData(multipartRequest.getFileBytes(clientId));
setFileContentType(multipartRequest.getFileContentType(clientId));
setFileName(multipartRequest.getFileName(clientId));
saveOpenAttachment();
}
}
Now I handle the request like Seam do, and do not need the web:multipart-filter config that was breaking other types of request.

DELETE methods not callable

I don't seem to be able to call DELETE methods via Restler for some reason. In my restler php file I've defined a method like so:
/**
* Drop invitation
*
* Removes an invitation from the system. Coaches want a way to be able
* to remove an invite if they messed up, or the person just doesn't accept.
*
* #param string $email The email address of the invited person {#from body}
* #param int $team_id The SQL identifier for the team {#from body}
*
* #return array An empty array
*/
public function deleteInvite($email, $team_id) {
return [];
}
When I try to call it:
curl -X DELETE -H "Content-Type: application/json" -d '{"email":"foo","team_id",17}' http://server.com/app/team/invite
It comes back with a 400 error:
{
"error": {
"code": 400,
"message": "Bad Request: email is missing."
}
}
How am I supposed to be calling this?
as a widely accepted practice restler does not support/read body parameters for GET and DELETE requests. Send them on url or as a query string instead.
As you can see in https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p2-semantics-26#section-4.3
HEAD - No defined body semantics.
GET - No defined body semantics.
PUT - Body supported.
POST - Body supported.
DELETE - No defined body semantics.
TRACE - Body not supported.
OPTIONS - Body supported but no semantics (maybe in the future).
Many http servers do not parse the body of GET and DELETE requests, restler is following that practice

Uploading a file in Jersey without using multipart

I run a web service where I convert a file from one file format into another. The conversion logic is already functioning but now, I want to query this logic via Jersey. Whenever file upload via Jersey is addressed in tutorials / questions, people describe how to do this using multipart form data. I do however simply want to send and return a single file and skip the overhead of sending multiple parts. (The webservice is triggered by another machine which I control so there is no HTML form involved.)
My question is how would I achieve something like the following:
#POST
#Path("{sessionId"}
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("sessionId") String sessionId,
#WhatToPutHere InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFile); // returns StreamingOutput - works!
}
How do I get hold of the uploadedFileStream (It should be some annotation, I guess which is of course not #WhatToPutHere). I figured out how to directly return a file via StreamingOutput.
Thanks for any help!
You do not have to put anything in the second param of the function; just leave it un-annoted.
The only thing you have to be carefull is to "name" the resource:
The resource should have an URI like: someSite/someRESTEndPoint/myResourceId so the function should be:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}
If you want to use some kind of SessionID, I'd prefer to use a Header Param... something like:
#POST
#Path("{myResourceId}")
#Consumes("image/png")
#Produces("application/pdf")
public Response put(#HeaderParam("sessionId") String sessionId,
#PathParam("myResourceId") String myResourceId,
InputStream uploadedFileStream) {
return BusinessLogic.convert(uploadedFileStream);
}

How to send base64 encoded file to PlayFramework server?

I'd like to implement a FileUpload using the new FileReader API. From the client side, everything works well and I can send a PUT request to the server with the correct fields containing the file in Base64 encoded.
But in the server side, it's not going great, here are my results :
Logger.info(String.valueOf(request().body().asRaw())); // null
Logger.info(String.valueOf(request().body().asText())); // null
And most importantly :
Logger.info(String.valueOf(request().body().isMaxSizeExceeded())); // true !
What am I missing? How can I make it work?
I found the answer to my question !
For those who are looking for it, here's the answer :
You need to add a BodyParser as annotation for your method, and specify a higher maxLength value.
#BodyParser.Of(value = BodyParser.Json.class, maxLength = 1024 * 1024)
public static Result method() {
Logger.info(String.valueOf(request().body().asJson())); // Will not be empty!
}