看起来您正在使用“立即上传”模式。这是未来寻求者的完整示例:
应用程序/视图/静态页面/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>
应用程序/资产/javascripts/main.js.coffee:
@app = angular.module 'myApp', ['angularFileUpload']
应用程序/资产/javascripts/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
]
注意:在上面的代码中,我必须添加 csrf 行,因为在 app/views/layouts/application.rb 中,我有这个:
<%= csrf_meta_tags %>
这会导致 rails 为每个网页添加一个 csrf 令牌。angular-file-upload 是导致的rails CSRF Errors
,所以我必须检索 csrf 令牌并将其添加到请求标头中。
应用程序/资产/javascripts/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 .
我没有将 gems 用于 angular 或 angular-file-upload。我只是将 AngularJS 代码复制到一个名为 angular.js 的文件中,我将它放在 app/assets/javascripts 中。同样,我将 angular-file-upload-all 中的代码复制到 app/assets/javascripts/angular-file-upload-all.js
应用程序/控制器/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
配置/路由.rb:
Test1::Application.routes.draw do
root "static_pages#index"
post "static_pages/upload"
据我所知,data:
关键需要是文件的内容(作为 ArrayBuffer)。要让 rails 在 params 哈希中插入其他数据,您可以使用 url,例如
url: "/media.json" + '?firstName=Kaspar
在服务器端,我可以访问该文件的唯一方法是使用request.body.read
带有request.headers['Content-Type']
. 你最后做了什么?
另外,我在这里发现了 file.type 的两个问题:
headers: {
'Content-Type': file.type,
1)由于某种原因,FireFox 和 Chrome 都无法确定 file 的文件类型.json
,因此file.type
最终成为一个空白字符串:""
. 然后,Rails 将文件的内容作为参数哈希中的键输入。嗯?
如果您添加.json
到网址的末尾:
url: "/static_pages/upload.json",
...然后 Rails 会将请求的主体解析为 JSON 并在 params 哈希中输入键/值对。但是添加.json
到 url 不会使代码非常通用,因为它会阻止其他文件类型被正确处理。
这是上传.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'
...然后在代码后面:
headers: {
'Content-Type': file_type, #instead of file.type
2)但是,原代码中仍然存在一个闭包问题,需要进行更正才能使多个文件选择正常工作。如果选择多个文件,则所有文件的 file_type 最终将是最后一个文件的 file_type。例如,如果您选择一个.txt
文件和一个.json
文件,那么这两个文件都将具有第二个文件的类型,即application/json
. 这是有问题的,因为 rails 会尝试将文本文件的主体解析为 JSON,这将产生错误ActionDispatch::ParamsParser::ParseError
。
为了纠正闭包问题,一个众所周知的解决方案是围绕 fileReader.onload() 定义一个包装函数。Coffeescript 的语法使得添加包装函数特别轻松:
do (file_type) -> #wrapper function, which `do` immediately executes sending it the argument file_type
fileReader.onload = (e) ->
...
...
通过添加一行,您可以解决共享变量问题。有关其作用的详细信息,请转到coffeescript 主页并在页面上搜索:do keyword
.
应用程序/资产/javascripts/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
]
最后,在这段代码中,
data: e.target.result
...返回的实体e.target.result
是ArrayBuffer
,我无法弄清楚如何修改它以添加其他数据。