{"action":"created","issue":{"url":"https://api.github.com/repos/revel/revel/issues/756","labels_url":"https://api.github.com/repos/revel/revel/issues/756/labels{/name}","comments_url":"https://api.github.com/repos/revel/revel/issues/756/comments","events_url":"https://api.github.com/repos/revel/revel/issues/756/events","html_url":"https://github.com/revel/revel/issues/756","id":45225284,"number":756,"title":"Binder: Multipart io.Reader parameters needs to be closed.","user":{"login":"klauspost","id":5663952,"avatar_url":"https://avatars.githubusercontent.com/u/5663952?v=3","gravatar_id":"","url":"https://api.github.com/users/klauspost","html_url":"https://github.com/klauspost","followers_url":"https://api.github.com/users/klauspost/followers","following_url":"https://api.github.com/users/klauspost/following{/other_user}","gists_url":"https://api.github.com/users/klauspost/gists{/gist_id}","starred_url":"https://api.github.com/users/klauspost/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/klauspost/subscriptions","organizations_url":"https://api.github.com/users/klauspost/orgs","repos_url":"https://api.github.com/users/klauspost/repos","events_url":"https://api.github.com/users/klauspost/events{/privacy}","received_events_url":"https://api.github.com/users/klauspost/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/revel/revel/labels/bug","name":"bug","color":"e10c02"},{"url":"https://api.github.com/repos/revel/revel/labels/controller","name":"controller","color":"eb6420"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/revel/revel/milestones/6","labels_url":"https://api.github.com/repos/revel/revel/milestones/6/labels","id":766323,"number":6,"title":"v0.12","description":null,"creator":{"login":"brendensoares","id":905501,"avatar_url":"https://avatars.githubusercontent.com/u/905501?v=3","gravatar_id":"","url":"https://api.github.com/users/brendensoares","html_url":"https://github.com/brendensoares","followers_url":"https://api.github.com/users/brendensoares/followers","following_url":"https://api.github.com/users/brendensoares/following{/other_user}","gists_url":"https://api.github.com/users/brendensoares/gists{/gist_id}","starred_url":"https://api.github.com/users/brendensoares/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brendensoares/subscriptions","organizations_url":"https://api.github.com/users/brendensoares/orgs","repos_url":"https://api.github.com/users/brendensoares/repos","events_url":"https://api.github.com/users/brendensoares/events{/privacy}","received_events_url":"https://api.github.com/users/brendensoares/received_events","type":"User","site_admin":false},"open_issues":26,"closed_issues":61,"state":"open","created_at":"2014-08-26T16:54:06Z","updated_at":"2015-01-07T09:37:59Z","due_on":null,"closed_at":null},"comments":7,"created_at":"2014-10-08T09:59:41Z","updated_at":"2015-01-07T11:53:05Z","closed_at":null,"body":"I have tracked down an issue with revel, where temporary files are leaked.\r\n\r\nI have a controller function like this:\r\n\r\n```func (c File) Replace(id string, file io.Reader, metadata string) revel.Result {```\r\n\r\nShort story is, if I upload a file larger than 32MB, I get this error:\r\n```params.go:121: Error removing temporary files: remove c:\\temp\\win\\multipart-265323653: The process cannot access the file, since it is in use by another process.```\r\n\r\nThe reason for this is, that the file is larger than 32MB, so \"req.ParseMultipartForm\" in \"params.go\" places the file on disk. Very reasonable.\r\n\r\nHowever, this also means the \"io.Reader\" must be closed for the system to be able to delete the file after processing it. This is not done.\r\n\r\nSo whenever Revel binds a parameter, it should check if it implements io.Closer and call it once the controller has been called.\r\n\r\nThe workarounds are:\r\n* Test if the supplied reader is io.Closer in the controller (this should be handled by Revel)\r\n* Use \"os.File\" for the controller. Silly, since that would copy it to disk every time."},"comment":{"url":"https://api.github.com/repos/revel/revel/issues/comments/69012314","html_url":"https://github.com/revel/revel/issues/756#issuecomment-69012314","issue_url":"https://api.github.com/repos/revel/revel/issues/756","id":69012314,"user":{"login":"klauspost","id":5663952,"avatar_url":"https://avatars.githubusercontent.com/u/5663952?v=3","gravatar_id":"","url":"https://api.github.com/users/klauspost","html_url":"https://github.com/klauspost","followers_url":"https://api.github.com/users/klauspost/followers","following_url":"https://api.github.com/users/klauspost/following{/other_user}","gists_url":"https://api.github.com/users/klauspost/gists{/gist_id}","starred_url":"https://api.github.com/users/klauspost/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/klauspost/subscriptions","organizations_url":"https://api.github.com/users/klauspost/orgs","repos_url":"https://api.github.com/users/klauspost/repos","events_url":"https://api.github.com/users/klauspost/events{/privacy}","received_events_url":"https://api.github.com/users/klauspost/received_events","type":"User","site_admin":false},"created_at":"2015-01-07T11:53:05Z","updated_at":"2015-01-07T11:53:05Z","body":"I had a look at it, and it seems like this could be handled by adding a simple check in the \"ActionInvoker\" controller:\r\n\r\n```Go\r\n\tfor _, arg := range c.MethodType.Args {\r\n\t\t// If they accept a websocket connection, treat that arg specially.\r\n\t\tvar boundArg reflect.Value\r\n\t\tif arg.Type == websocketType {\r\n\t\t\tboundArg = reflect.ValueOf(c.Request.Websocket)\r\n\t\t} else {\r\n\t\t\tboundArg = Bind(c.Params, arg.Name, arg.Type)\r\n\t\t\t// If the argument is a closer, defer a Close call, so we don't risk leaks.\r\n\t\t\tcloser, ok := boundArg.Interface().(io.Closer)\r\n\t\t\tif ok {\r\n\t\t\t\tdefer closer.Close()\r\n\t\t\t}\r\n\t\t}\r\n\t\tmethodArgs = append(methodArgs, boundArg)\r\n\t}\r\n```\r\nThis fixes the issue for me.\r\n\r\n\"bindByteArray\" should just be closed in the binder after the file has been read.\r\n\r\n\r\nTo reproduce:\r\n* Add POST controller, similar to this:\r\n```func (c File) Add(file io.Reader) revel.Result {\r\n  return c.RenderJson(\"Success\")\r\n}\r\n``\r\n* Send a file >32MB with a POST.\r\nThe temporary file will now remain visible on Linux, and the error above will show on Windows.\r\n\r\n"}}