Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ajax Uploads #84

Closed
psrpinto opened this issue Dec 8, 2012 · 18 comments
Closed

Ajax Uploads #84

psrpinto opened this issue Dec 8, 2012 · 18 comments

Comments

@psrpinto
Copy link

psrpinto commented Dec 8, 2012

I'm uploading a file through an Ajax Request and I would like to use this bundle to manage it. The problem is that my file is not an UploadedFile but a Symfony\Component\HttpFoundation\File\File.

So, basically, I save the file to disk, create an instance of Symfony\Component\HttpFoundation\File\File and then attach it to my entity that has the proper UploadableField annotation.

I took a look at the code and I saw that the File needs to be an instance of UploadedFile, in AbstractStorage.php:

if (is_null($file) || !($file instanceof UploadedFile)) {
    continue;
}

Is there a reason for the bundle to require the file to be an UploadedFile instead of a plain File? The way I see it, that fact considerably restricts the usefulness of this bundle because it's limited to files uploaded through a Form. I'm guessing I'm not the only one doing Ajax uploads in Symfony2 and that other people might benefit from not requiring the file to be an instance of UploadedFile.

@ftassi
Copy link
Collaborator

ftassi commented Dec 8, 2012

If you have a File instance and not an UploadedFile one you've already uploaded the file on the server, what you should need the UploadableField field for ?

@psrpinto
Copy link
Author

psrpinto commented Dec 8, 2012

@ftassi Because I would like to use the bundle to manage the lifecycle of the file. For instance, for the file to be deleted when the entity is destroyed and to use other functionalities like the template helpers.

@ftassi
Copy link
Collaborator

ftassi commented Dec 8, 2012

Cool, so the AbstractStorage::upload need to do nothing in your case, and that is what that if does (it just skip the upload if the entity doesn't contains an UploadedFile).

What am I missing ?

@ftassi
Copy link
Collaborator

ftassi commented Dec 8, 2012

I mean that if you take care of the upload, you need store a File instance within the Entity. The rest of the bundle's code should work, as long as you bind a valid File. That if is just preventing the upload, which is fine for you, because you've already uploaded the file

@psrpinto
Copy link
Author

psrpinto commented Dec 8, 2012

Two things:

I would like the bundle to store the file in the location that is defined in my config file:

project_image:
    upload_destination: %kernel.root_dir%/../web/uploads/project/images

I would like the image_name field to be automatically set:

@Vich\UploadableField(
    mapping="project_image",
    fileNameProperty="image_name"
)

@psrpinto
Copy link
Author

psrpinto commented Dec 8, 2012

Just to make it clear, the idea is to have a generic Ajax Uploader (my code) that saves a temporary version of the file. Then, according to what type of entity the File is attached to it will be moved to the location that was specified in the config file (upload_destination) for said entity. Also, I might implement a Namer and I want the image_name field of my entity to be set accordingly.

@ftassi
Copy link
Collaborator

ftassi commented Dec 8, 2012

mmm you want to rename the file after is uploaded then, we are not talking about uploading the file, we are talking about moving it (or renaming). This not what this bundle is for. You can probably achieve this reusing some part of the bundle's code, but this will not work out of the box.

I think the best way should be to let the bundle manage the upload process, so you'll get what you need for free. I don't see how an ajax upload should be different from a non ajax one. At certain point you'll have an UploadedFile in your controller, what you need to do is bind that UploadedFile instance to the entity and then save the entity.

This binding step is usually done by the form (I'm talking about a Symfony's form here), but you didn't really need a form instance, you can do it manually if you need to.

Make sense ?

@psrpinto
Copy link
Author

psrpinto commented Dec 8, 2012

mmm you want to rename the file after is uploaded then, we are not talking about uploading the file, we are talking about moving it (or renaming). This not what this bundle is for.

If I understood correctly, moving the file is what the bundle does anyway for an UploadedFile. An UploadedFile is just a regular file that was stored under /tmp/phpsomething by PHP and that is then moved by the bundle to the directory configured in upload_destination:

protected function doUpload(UploadedFile $file, $dir, $name)
{
    $uploadDir = $this->getUploadDirectory($dir, $name);
    $fileName = basename($name);
    return $file->move($uploadDir, $fileName);
}

I don't see how an ajax upload should be different from a non ajax one. At certain point you'll have an UploadedFile in your controller, what you need to do is bind that UploadedFile instance to the entity and then save the entity.

For an Ajax upload, the is_uploaded_file function returns false and, thus, the move method of UploadedFile (which is called by doUpload) will throw an exception:

public function move($directory, $name = null)
{
    if ($this->isValid() && ($this->test || is_uploaded_file($this->getPathname()))) {
        return parent::move($directory, $name);
    }

    throw new FileException(sprintf('The file "%s" has not been uploaded via Http', $this->getPathname()));
}

I might be missing something here...

You can probably achieve this reusing some part of the bundle's code, but this will not work out of the box.

I just made a quick test (FileSystemStorage only), replacing UploadedFile by File and everything seems to work.

@ftassi
Copy link
Collaborator

ftassi commented Dec 9, 2012

For an Ajax upload, the is_uploaded_file function returns false and, thus, the move method of UploadedFile (which is called by doUpload) will throw an exception:

I don't think that is the ajax upload the problem, but instead the fact that you're using the upload method too late, not for "uploading" the file but for "moving" an uploaded file.

UploadedFile VS File is correct way to check if a file needed to be uploaded or if it just the file reference for an Entity loaded from DB (imho). I don't know how your code is working but I'm pretty sure that you have a controller that handle the actual file upload. That point is were you should have an UploadedFile, and that point is were you should bind it to the Entity in order to trigger the upload mechanism of the bundle.

If you need a more specific help with this I'll need you to share your code with me. We can even add some example on how to manage this kind of use case in the doc (I think Ajax upload is a pretty much common requirement).

BTW I'm closing this as is nor a bug report or a feature request. If you want to go further we can post here the solution of your problem and then integrate that in the doc somehow.

@ftassi ftassi closed this as completed Dec 9, 2012
@zabojad
Copy link

zabojad commented Jan 10, 2013

Is there finally any documentation on how to implement correctly an Ajax upload with VichUploaderBundle ?

@ftassi
Copy link
Collaborator

ftassi commented Jan 10, 2013

Not really. I've never used the bundle for ajax upload, if you have some example we can update the doc. By the way, how the file get to the server is really not important, as long as the entity has the UploadedFile instance, the bundle should work as expected.

@zabojad
Copy link

zabojad commented Jan 10, 2013

I'll try to propose my own solution soon. Would be happy to have your 2 cents on it then...

@ftassi
Copy link
Collaborator

ftassi commented Jan 10, 2013

👍 share it and we can discuss it

@zabojad
Copy link

zabojad commented Jan 11, 2013

Hi, can you have a look at my last comment on this issue: #96 (comment)

I'm having a problem preventing me from uploading with Ajax. Indeed, If I send a POST request (with AJAX or not) to upload the image without changing any other filed of my entity, it doesn't get uploaded (not saved on server side FS nor updated in the DB).

Is that normal behavior ? (if yes, why?)

Is there something to do to make it uploading the image even when not changing any other field of my entity ?

Thanks in advance...

@tobiassjosten
Copy link

I needed to download a file in a command, so the is_uploaded_file() restriction hit me too. What I did was subclass UploadedFile and overwrite its isValid() method to always return true.

Then I could download the file to /tmp/whatever, instantiate a FakeUploadedFile object and have this bundle work its magic as if the file was uploaded through a normal form.

Maybe it'd have been cleaner to use the $test property of UploadedFile but it felt like the wrong use case for that.

@Baachi
Copy link
Contributor

Baachi commented Sep 17, 2013

@tobiassjosten Symfony provide already such a behaviour. Look at the 6th parameter

@tobiassjosten
Copy link

Yes, the $test parameter.

Maybe it'd have been cleaner to use the $test property of UploadedFile but it felt like the wrong use case for that.

:)

But looking at it now, it seems that parameter not only changes the behavior of isValid(), but also move(). With $test being false (default) it uses move_uploaded_file() to move the file, which seems to have the same HTTP upload check as is_uploaded_file(). So I'm wondering why it worked for me.

Still, switching to using UploadedFile with $test enabled worked fine and it seems to be the way to go. Thanks for making me have a second look, @Baachi.

@haithemkdous
Copy link

@zabojad @regularjack have you find any solution to use Ajax upload with VichUploaderBundle??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants