mikek
mikek

Reputation: 169

file upload objects across controller actions

Running Grails 2.3.3 and I have a multiple file input requirement and need to transfer the selected file objects across from one action to another - the 1st action captures the file list from a multiple 'input' tag. This data is sent to the 2nd action via 'params'. Through params I can access a string representation of the file objects in the 2nd action.

There I explicitly cast them into list of type MultipartFile using the definition:

def <MultpartFile> []

and then placing each file object into the list.

When I print out the file objects they are displayed in the form:

org.springframework.web.multipart.commons.CommonsMultipartFile@25af2936

This being identical to that displayed from the 1st action.

But when trying to use the elements of the object in the 2nd action such as file.originalFilename I get a MissingPropertyException error:

No such property: getOriginalFilename for class: java.lang.String

Is there a way to get this string into the appropriate type to do a file transfer as ultimately I want to be able to transfer (upload) the selected files from this 2nd action. Or does any file transfer need to be done in the original action that linked to the view with the multiple file input tag?

FUTHER EXPLANATION:

I didn't fully explain the flow logic. I am not calling one action from another directly but doing it via buttons on a view. In a view I get a set of files from an html multiple input tag then go to a 'save' action from the view submitButton this action create another view that displays these selected files and enables the user to add some extra descriptive tags. In this save view there is another submitbutton that goes to the upload action that is meant collect all this file information and upload the files (availing of the MultipartFile object for each file to do the file transfer) along with the additional data supplied by the user.

-mike

Upvotes: 0

Views: 422

Answers (1)

Burt Beckwith
Burt Beckwith

Reputation: 75671

That's not how it works - you almost never want one controller action calling another. And you can't pack the entire contents of params into the querystring for a redirect. For starters there's a limit to the size of the querystring (I don't know what it is, and I don't care - I don't expect to ever come close to the limit), so if you have a file that's not tiny, you'll lose most of it in the redirect. You also can't just dump any old data type into the querystring - you need to serialize in a format that allows you to deserialize on the other side without loss, and a simple toString() call is pretty much never that format, except for numbes, booleans, and strings.

Controllers are called by Grails and the Spring MVC code that handles requests. If you want to do the work from two controller actions during one request, then extract the non-HTTP code to another class that isn't called as indirectly as controller actions are, e.g. a service.

I refactor persistence and business logic into a service immediately - controllers should be dumb routers; they handle requests, do some databinding of querystring args and form body data,, determine which helper(s) to call to do the work for this action (likely services and/or domain classes) and then when the call(s) to the helper(s) finish, transition to the next page (using redirect or forward) or render a response.

It's also very important that data be written transactionally, and services are transactional by default so they're a great place for that code. Ignore the @Transactional annotations in your Grails 2.3 and 2.4 controllers - they make that code slightly better, but it's still entirely in the wrong place.

Even if you don't refactor your controllers immediately, should as soon as its methods get a bit large, or they have too many methods (or multiple groups of actions that are related, and which should each have their own controller), or you do database writes and business logic. That probably sounds like every controller in every app you've worked on - we need to do a better job getting Grails developers to not think like PHP developers, dumping stuff wherever it's convenient, or wherever the code generation script seems to imply that things go.

So to address your specific needs - refactor those two controllers into two (or more if it makes sense) methods in a service. Extract the file upload data from the first controller action and do the same work you are now, but in the service and not the controller. This service method can call as many other methods as needed, and never with something as clumsy as an HTTP redirect - just a plain old method call.

one last thing - just like database writes and business logic don't belong in controollers, you similarly don't want the service tier to know about HTTP. That means don't pass over the request, response, session, or even params to a service method. Extract out the data that you need from them and use that in method signatures. It's unlikely that you'll reuse the service classes independently from the controllers, taglibs, domain classes, etc., you can if you don't couple the tiers. And if you keep the HTTP-specific code out of srervices they become much more portable, and easier to test since there's less junk in the way to get to what you're trying to verify.

Upvotes: 1

Related Questions