[英]Not able to upload a file to Rails server with Angular
So I am using the gem 'angular-file-upload-rails'
which installs me this Angular plugin: Angular File Ipload 所以我正在使用gem'angular gem 'angular-file-upload-rails'
,它为我安装了这个Angular插件: Angular File Ipload
Now the code I am using currently to upload my file looks like this: 现在,我当前用于上传文件的代码如下所示:
HTML: HTML:
<form ng-controller="MediumNewCtrl">
<input type="file" ng-file-select="upload2($files)" multiple>
</form>
Coffescript: Coffescript:
$scope.upload2 = ($file) ->
console.log($file[0])
fileReader = new FileReader()
fileReader.readAsArrayBuffer($file[0])
fileReader.onload = (e) ->
$upload.http(
url: "/media.json"
headers: 'Content-Type': $file[0].type
data: medium: {text: 'text', image_video: e.target.result}
).progress((evt) ->
console.log "percent: " + parseInt(100.0 * evt.loaded / evt.total)
return
).success((data, status, headers, config) ->
# file is uploaded successfully
console.log data
).error((data) ->
console.log 'Error'
)
And now when I look at what my server responded, I see this: 现在,当我查看服务器响应时,会看到以下内容:
Started POST "/media.json" for 127.0.0.1 at 2014-12-12 20:19:10 +0200
Processing by Content::MediaController#create as JSON
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
{"action"=>"create", "controller"=>"content/media", "format"=>"json"}
Completed 400 Bad Request in 3ms
ActionController::ParameterMissing - param is missing or the value is empty: medium:
Is the problem in the fact that I format it as json? 问题是我将其格式化为json吗? But shouldnt atleast the text params be passed to the controller? 但是,是否应至少将文本参数传递给控制器?
I cannot use the html Post too because the nature of my application is so that it will intercept all HTML requests when you log in. 我也不能使用html Post,因为我的应用程序的性质是这样,以便您登录时它将拦截所有HTML请求。
Also maybe worth nothing that I use paperclip
to manage my uploads for me. 另外,使用paperclip
为我管理上传内容可能一文不值。 So I probably have to get the file sent into a proper format too for it? 因此,我可能也必须将文件发送为正确的格式吗?
It looks like you are using the 'upload right away' pattern. 您似乎正在使用“立即上传”模式。 Here is a complete example for future seekers: 这是面向未来求职者的完整示例:
app/views/static-pages/index.html: 应用程序/视图/静态页/ index.html的:
<div ng-app='myApp'>
<h1>StaticPages#index</h1>
<p>Find me in: app/views/static_pages/index.html.erb</p>
<hr>
<div ng-controller="FileUploadCtrl">
<input type="file"
ng-file-select=""
ng-model='selectedFiles'
ng-file-change="myUpload(selectedFiles)"
ng-multiple="true">
</div>
</div>
app/assets/javascripts/main.js.coffee: 应用程序/资产/ Java脚本/ main.js.coffee:
@app = angular.module 'myApp', ['angularFileUpload']
app/assets/javascripts/FileUploadCtrl.js.coffee: 应用程序/资产/ Java脚本/ FileUploadCtrl.js.coffee:
@app.controller 'FileUploadCtrl', [
'$scope',
'$upload',
'$timeout',
($scope, $upload, $timeout) ->
$scope.myUpload = (files) ->
len = files.length
i = 0
fileReader = undefined
csrf_token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
for file in files
fileReader = new FileReader()
#-------
fileReader.onload = (e) ->
#Define function for timeout, e.g. $timeout(timeout_func, 5000)
timeout_func = ->
file.upload = $upload.http {
url: "/static_pages/upload",
method: 'POST',
headers: {
'Content-Type': file.type,
'X-CSRF-TOKEN': csrf_token
},
data: e.target.result #the file's contents as an ArrayBuffer
}
file.upload.then(
(success_resp) -> file.result = success_resp.data, #response from server (=upload.html)
(failure_resp) ->
if failure_resp.status > 0
$scope.errorMsg = "#{failure_resp.status}: #{response.data}"
)
file.upload.progress( (evt) ->
file.progress = Math.min 100, parseInt(100.0 * evt.loaded / evt.total)
)
#end of timeout_func
$timeout timeout_func, 5000
#end of FileReader.onload
fileReader.readAsArrayBuffer file
]
Note: In the code above, I had to add the csrf lines because in app/views/layouts/application.rb, I have this: 注意:在上面的代码中,我必须添加csrf行,因为在app / views / layouts / application.rb中,我具有以下内容:
<%= csrf_meta_tags %>
which causes rails to add a csrf token to each web page. 这会导致Rails向每个网页添加csrf令牌。 angular-file-upload was causing rails CSRF Errors
, so I had to retrieve the csrf token and add it to the request headers. angular-file-upload导致rails CSRF Errors
,所以我不得不检索csrf令牌并将其添加到请求标头中。
app/assets/javascripts/application.js: 应用程序/资产/ Java脚本/ application.js中:
//I removed:
// require turbolinks
//for angular app
//
//= require jquery
//= require jquery_ujs
//
//The 'require_tree .' adds all the files in some random
//order, but for AngularJS the order is important:
//
//= require angular
//= require angular-file-upload-all
//
//And for some inexplicable reason, this is needed:
//= require main
//I would think 'require_tree .' would be enough for that file.
//
//= require_tree .
I didn't use gems for angular or angular-file-upload. 我没有使用gem进行角度上传或角度文件上传。 I just copied the AngularJS code into a file named angular.js which I put inside app/assets/javascripts. 我只是将AngularJS代码复制到名为angular.js的文件中,并将其放入app / assets / javascripts中。 Similarly, I copied the code in angular-file-upload-all into app/assets/javascripts/angular-file-upload-all.js 同样,我将angular-file-upload-all中的代码复制到app / assets / javascripts / angular-file-upload-all.js中
app/controllers/static_pages_controller.rb: 应用程序/控制器/ static_pages_controller.rb:
class StaticPagesController < ApplicationController
def index
end
def upload
puts "****PARAMS:"
p params
puts "****body of request: #{request.body.read.inspect}" #inspect => outputs "" for empty body rather than nothing
puts "****Content-Type: #{request.headers['Content-Type']}"
render nothing: true
end
end
config/routes.rb: 配置/ routes.rb文件:
Test1::Application.routes.draw do
root "static_pages#index"
post "static_pages/upload"
As far as I can tell the data:
key needs to be the contents of the file (as an ArrayBuffer). 据我所知, data:
键必须是文件的内容(作为ArrayBuffer)。 To get rails to insert additional data in the params hash, you could use the url, for example 要使rails在params哈希中插入其他数据,可以使用url,例如
url: "/media.json" + '?firstName=Kaspar
On the server side, the only way I could access the file was using request.body.read
and the headers with request.headers['Content-Type']
. 在服务器端,访问文件的唯一方法是使用request.body.read
和带有request.headers['Content-Type']
的标头。 What did you end up doing? 你最终做了什么?
headers: {
'Content-Type': file.type,
1) For some reason, neither FireFox nor Chrome can determine the file type of a .json
file , so file.type
ends up being a blank string: ""
. 1)由于某些原因,既不火狐也不铬可以确定的文件类型.json
文件 ,所以file.type
最终被一个空字符串: ""
。 Rails then enters the file's contents as a key in the params hash. 然后,Rails在params哈希中输入文件的内容作为键 。 Huh? 咦?
If you tack .json
onto the end of the url: 如果将.json
粘贴到网址末尾:
url: "/static_pages/upload.json",
...then Rails will parse the body of the request as JSON and enter the key/value pairs in the params hash. ...然后,Rails将请求的主体解析为JSON,并在params哈希中输入键/值对。 But adding .json
to the url doesn't make the code very general because it prevents other file types from being processed correctly. 但是将.json
添加到url不会使代码变得非常通用,因为它会阻止其他文件类型的正确处理。
Here is a more general solution for uploading .json
files: 这是上传.json
文件的更通用的解决方案:
for file in files
file_type = file.type
if file_type is '' #is => ===
[..., file_ext] = file.name.split '.'
if file_ext is 'json'
file_type = 'application/json'
...then later in the code: ...然后在代码后面:
headers: {
'Content-Type': file_type, #instead of file.type
2) However, there is still a closure problem in the original code, which needs to be corrected in order for multiple file selections to work correctly. 2)但是,原始代码中仍然存在关闭问题 ,需要纠正此问题才能使多个文件选择正常工作。 If you select multiple files, then the file_type for all the files will end up being the file_type of the last file. 如果选择多个文件,则所有文件的file_type将最终成为最后一个文件的file_type。 For instance, if you select a .txt
file and a .json
file, then both files will have the type of the second file, ie application/json
. 例如,如果您选择.txt
文件和.json
文件,则两个文件都将具有第二个文件的类型,即application/json
。 That's problematic because rails will try to parse the body of the text file as JSON, which will produce the error ActionDispatch::ParamsParser::ParseError
. 这是有问题的,因为rails会尝试将文本文件的主体解析为JSON,这将产生错误ActionDispatch::ParamsParser::ParseError
。
To correct the closure problem, one well known solution is to define a wrapper function around fileReader.onload(). 为了纠正关闭问题,一种众所周知的解决方案是围绕fileReader.onload()定义包装函数。 Coffeescript has a syntax that makes adding a wrapper function especially pain free: Coffeescript的语法使添加包装器函数特别轻松:
do (file_type) -> #wrapper function, which `do` immediately executes sending it the argument file_type
fileReader.onload = (e) ->
...
...
By adding one line, you can fix the shared variable problem. 通过添加一行,可以解决共享变量问题。 For details on what that does, go to the coffeescript home page and search the page for: do keyword
. 有关执行该操作的详细信息,请转到coffeescript主页并在页面上搜索: do keyword
。
app/assets/javascripts/FileUploadCtrl.js.coffee: 应用程序/资产/ Java脚本/ FileUploadCtrl.js.coffee:
@app.controller 'FileUploadCtrl', [
'$scope',
'$upload',
'$timeout',
($scope, $upload, $timeout) ->
$scope.myUpload = (files) ->
len = files.length
i = 0
fileReader = undefined
csrf_token = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
for file in files
#console.log file
file_type = file.type
#console.log file_type
if file_type is ''
[..., file_ext] = file.name.split '.'
#console.log file_ext
if file_ext is 'json'
file_type = 'application/json'
#console.log "Corrected file_type: " + file_type
fileReader = new FileReader()
#-------
do (file_type) ->
fileReader.onload = (e) ->
#Define function for timeout, e.g. $timeout(timeout_func, 5000)
timeout_func = ->
file.upload = $upload.http(
url: "/static_pages/upload"
method: 'POST'
headers:
'Content-Type': file_type
'X-CSRF-TOKEN': csrf_token
data: e.target.result, #file contents as ArrayBuffer
)
file.upload.then(
(success_resp) -> file.result = success_resp.data, #response from server
(failure_resp) ->
if failure_resp.status > 0
$scope.errorMsg = "#{failure_resp.status}: #{response.data}"
)
file.upload.progress (evt) ->
file.progress = Math.min 100, parseInt(100.0 * evt.loaded / evt.total)
#end of timeout_func
$timeout timeout_func, 5000
#end of FileReader.onload
fileReader.readAsArrayBuffer file
]
Finally, in this code, 最后,在这段代码中
data: e.target.result
...the entity returned by e.target.result
is an ArrayBuffer
, and I wasn't able to figure out how to modify that to add additional data. ...由e.target.result
返回的实体是ArrayBuffer
,我无法弄清楚如何修改它以添加其他数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.