file_upload 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +34 -0
- data/app/assets/javascripts/file_upload/application.js +13 -0
- data/app/assets/javascripts/file_upload.js.erb +148 -0
- data/app/assets/stylesheets/file_upload/application.css +13 -0
- data/app/assets/stylesheets/file_upload.css +3 -0
- data/app/controllers/file_upload/application_controller.rb +4 -0
- data/app/controllers/file_upload/redis_files_controller.rb +54 -0
- data/app/helpers/file_upload/application_helper.rb +4 -0
- data/app/helpers/file_upload/tag_helper.rb +25 -0
- data/app/models/file_upload/builder_mixin.rb +13 -0
- data/app/models/file_upload/redis_file.rb +159 -0
- data/app/models/file_upload/redis_file_readable.rb +21 -0
- data/app/models/file_upload/temp_file.rb +17 -0
- data/app/views/file_upload/redis_files/index.html.erb +1 -0
- data/app/views/file_upload/redis_files/new.html.erb +9 -0
- data/app/views/file_upload/redis_files/show.html.erb +15 -0
- data/app/views/file_upload/tags/_multiple.html.erb +44 -0
- data/app/views/file_upload/tags/_single.html.erb +46 -0
- data/app/views/layouts/file_upload/application.html.erb +14 -0
- data/config/routes.rb +6 -0
- data/lib/file_upload/engine.rb +40 -0
- data/lib/file_upload/version.rb +3 -0
- data/lib/file_upload.rb +7 -0
- data/lib/tasks/file_upload_tasks.rake +4 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/javascripts/jquery-1.10.2.min.js +6 -0
- data/test/dummy/app/assets/javascripts/jquery-ui.js +15003 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/db_files_controller.rb +8 -0
- data/test/dummy/app/controllers/emails_controller.rb +43 -0
- data/test/dummy/app/controllers/users_controller.rb +42 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/db_file.rb +13 -0
- data/test/dummy/app/models/email.rb +10 -0
- data/test/dummy/app/models/user.rb +9 -0
- data/test/dummy/app/views/db_files/_db_file.html.erb +1 -0
- data/test/dummy/app/views/emails/_email.html.erb +3 -0
- data/test/dummy/app/views/emails/_form.html.erb +24 -0
- data/test/dummy/app/views/emails/edit.html.erb +3 -0
- data/test/dummy/app/views/emails/index.html.erb +5 -0
- data/test/dummy/app/views/emails/new.html.erb +3 -0
- data/test/dummy/app/views/emails/show.html.erb +14 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/users/_form.html.erb +20 -0
- data/test/dummy/app/views/users/_user.html.erb +1 -0
- data/test/dummy/app/views/users/edit.html.erb +3 -0
- data/test/dummy/app/views/users/index.html.erb +5 -0
- data/test/dummy/app/views/users/new.html.erb +3 -0
- data/test/dummy/app/views/users/show.html.erb +8 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +28 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +31 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +7 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20130820025256_add_users.rb +21 -0
- data/test/dummy/db/schema.rb +34 -0
- data/test/dummy/log/development.log +15541 -0
- data/test/dummy/log/test.log +26004 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/1027254ad9f9317017cee3f9f844a862 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/21e0ff88cf13ca60a86a9b629cdca847 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/24cf35cebd10e0755a5585f9e486aa35 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/27413a86db5db5977d5dd1d87c724907 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/2f5c6d9c2c3107f94ad59710d3b31f75 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/3131541e8089635bc8595e2da83d1d81 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/32f1d616324cf52e9876bb83b9b0841a +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/33474263b3841cd98ef63cde45dc85b2 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/3fa0d13a4820d4aa56f86ed4af4b88fc +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/4e07f6f4475a0dca9066eec990db088a +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/5512d0b5788ce193fd26bfadccd21304 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/5d5ab91fa7d1eee7502e703747d4e933 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/6b7e97595c37161f619adfd2b7724ea2 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/87fa3cc4e2e49762d146f3d1709b3fb0 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/8806d06f37e08b22f9546e50ab98d671 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/8cdad172c92eaf76e1b71c84f58a192e +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/91e53bdf30a2b8e78bf99ca794addf70 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/96e2b2964d1d31d997a1af35ad0afed2 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ab5c07d7e81fb5d5cf7460715f22bef6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/b94837629e2fb2ef40ea66348764a9a2 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/c360bf7b66d29ebdf71b5814574b5fed +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/e046b0166566aeeb2da51f2bde543364 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/ec2d15616d5d09eb1693b761c72948ce +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/fc0327db3eb7c687c31f2acf94871f86 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/1027254ad9f9317017cee3f9f844a862 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/21e0ff88cf13ca60a86a9b629cdca847 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/24cf35cebd10e0755a5585f9e486aa35 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/27413a86db5db5977d5dd1d87c724907 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/32f1d616324cf52e9876bb83b9b0841a +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/3fa0d13a4820d4aa56f86ed4af4b88fc +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/5d5ab91fa7d1eee7502e703747d4e933 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/6b7e97595c37161f619adfd2b7724ea2 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/87fa3cc4e2e49762d146f3d1709b3fb0 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/8806d06f37e08b22f9546e50ab98d671 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/ab5c07d7e81fb5d5cf7460715f22bef6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/b94837629e2fb2ef40ea66348764a9a2 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/c360bf7b66d29ebdf71b5814574b5fed +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/e046b0166566aeeb2da51f2bde543364 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/ec2d15616d5d09eb1693b761c72948ce +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/test/dummy/tmp/cache/assets/test/sprockets/fc0327db3eb7c687c31f2acf94871f86 +0 -0
- data/test/fixtures/avatar.jpeg +0 -0
- data/test/fixtures/test_upload.txt +1 -0
- data/test/integration/multiple_upload_test.rb +131 -0
- data/test/integration/single_upload_test.rb +157 -0
- data/test/test_helper.rb +60 -0
- data/test/unit/redis_file_readable_test.rb +30 -0
- data/test/unit/redis_file_test.rb +30 -0
- data/vendor/assets/javascripts/jquery.fileupload.js +1113 -0
- data/vendor/assets/javascripts/load-image.js +232 -0
- metadata +401 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 411251a54632d2f6b1e5947507780ec4e303e438
|
4
|
+
data.tar.gz: 6cba04bb20df8f7125541cb1727ca43970c4a8df
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fa9a5651d4becd0b90d1c583fd883c9bb8e3d79221f0915157499c02a83be1266d21176c1d1b87e6676c98fe6922b171e13ed456fdf3365c19aea643ad6c2491
|
7
|
+
data.tar.gz: 1634bafd7a0c24478d7481ebfc873304a05283c91e79baad50fe0dddf77e02073fc6a9f07063d6ec07147a32f7c92397d0a2c60a123bbb810fa8cc60be142a74
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'FileUpload'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,148 @@
|
|
1
|
+
//= require load-image
|
2
|
+
//= require jquery.fileupload
|
3
|
+
|
4
|
+
$(function() {
|
5
|
+
// prevent default browser drapdrop behavior
|
6
|
+
$(document).bind('drop dragover', function (e) {
|
7
|
+
e.preventDefault();
|
8
|
+
});
|
9
|
+
|
10
|
+
function setProgress(item, percent) {
|
11
|
+
item.attr('aria-valuenow', percent)
|
12
|
+
.find(".bar").css("width", percent + "%");
|
13
|
+
}
|
14
|
+
|
15
|
+
function setupFileField(inp) {
|
16
|
+
var input = $(inp),
|
17
|
+
form = input.parents("form"),
|
18
|
+
attributeName = input.attr("data-attr-name"),
|
19
|
+
modelName = input.attr("data-model-name"),
|
20
|
+
container = form.find("." + attributeName),
|
21
|
+
list = container.find("ul"),
|
22
|
+
multiple = input.attr("multiple"),
|
23
|
+
preview = form.find(input.attr("data-preview") || ".preview"),
|
24
|
+
add = form.find(".add"),
|
25
|
+
inputPrefix = modelName + "[" + attributeName + "_attributes]",
|
26
|
+
inputName,
|
27
|
+
autosubmit = input.attr("data-autosubmit"),
|
28
|
+
url = input.attr("data-file-upload");
|
29
|
+
|
30
|
+
if(multiple) {
|
31
|
+
inputName = inputPrefix + "[REPLACE_ID][key]";
|
32
|
+
} else {
|
33
|
+
inputName = inputPrefix + "[key]";
|
34
|
+
}
|
35
|
+
|
36
|
+
input.fileupload({
|
37
|
+
url: url,
|
38
|
+
dataType: 'json',
|
39
|
+
maxFileSize: 5000000,
|
40
|
+
formData: function(form) { return []; },
|
41
|
+
type: 'POST',
|
42
|
+
// this is called after the files have been uploaded
|
43
|
+
done: function(e, data) {
|
44
|
+
// replace the input with a new one
|
45
|
+
replaceInput(this);
|
46
|
+
|
47
|
+
if(preview.length > 0) {
|
48
|
+
window.loadImage(data.files[0], function(img) {
|
49
|
+
preview.html(img);
|
50
|
+
});
|
51
|
+
}
|
52
|
+
// add field with file id
|
53
|
+
$.each(data.result.files, function(i, file) {
|
54
|
+
// load preview
|
55
|
+
if(multiple) {
|
56
|
+
// add checkbox field
|
57
|
+
var item = data.files[i].item;
|
58
|
+
item.find(".progress").replaceWith(
|
59
|
+
$('<input type="checkbox" checked="checked"/>')
|
60
|
+
.attr('name', inputName.replace("REPLACE_ID", Math.floor(Math.random() * 1000000)))
|
61
|
+
.val(file.id)
|
62
|
+
);
|
63
|
+
} else {
|
64
|
+
// add key hidden field
|
65
|
+
container.find(".inputs .progress").replaceWith('<input type="hidden" name="' + inputName + '" value="' + file.id + '"/><input type="checkbox" checked="checked" name="' + inputPrefix + "[_destroy]" + '" value="0"/>');
|
66
|
+
}
|
67
|
+
});
|
68
|
+
|
69
|
+
if(autosubmit) {
|
70
|
+
form.submit();
|
71
|
+
}
|
72
|
+
},
|
73
|
+
fail: function(e, data){
|
74
|
+
replaceInput(this);
|
75
|
+
|
76
|
+
if (data.jqXHR.status === 413) {
|
77
|
+
message = 'The size limit is 10MB.';
|
78
|
+
} else {
|
79
|
+
message = 'Upload failed, please try again.';
|
80
|
+
}
|
81
|
+
container.find('.error').html(message);
|
82
|
+
|
83
|
+
// data.files is 1-length array with the file of the ajax event
|
84
|
+
$.each(data.files, function(i, file) {
|
85
|
+
var item = data.files[i].item;
|
86
|
+
item.remove();
|
87
|
+
});
|
88
|
+
},
|
89
|
+
// this is called after selecting a file
|
90
|
+
add: function(e, data) {
|
91
|
+
// clear errors
|
92
|
+
container.find('.error').html('');
|
93
|
+
|
94
|
+
$.each(data.files, function(i, file) {
|
95
|
+
if(multiple) {
|
96
|
+
file.item = $("<li/>").append('<label class="checkbox"></label>');
|
97
|
+
file.item.find('label').text(file.name + " (" + (Math.round(file.size/10.24)/100) + "kB)" )
|
98
|
+
.prepend($("<div class='progress'><div class='bar'></div></div>"));
|
99
|
+
file.item.appendTo(list);
|
100
|
+
} else {
|
101
|
+
var label = $("<label class='checkbox'></label").text(file.name + " (" + (Math.round(file.size/10.24)/100) + "kB)").prepend("<div class='progress'><div class='bar'></div></div>");
|
102
|
+
container.find(".inputs").html(label);
|
103
|
+
}
|
104
|
+
});
|
105
|
+
|
106
|
+
// do the ajax file upload
|
107
|
+
data.submit();
|
108
|
+
}
|
109
|
+
});
|
110
|
+
|
111
|
+
// file type limiting option
|
112
|
+
if(input.attr("data-accept")) {
|
113
|
+
input.fileupload("option", "acceptFileTypes", new RegExp(input.attr("data-accept"), "i"));
|
114
|
+
}
|
115
|
+
|
116
|
+
if(!add.data("input")) {
|
117
|
+
add.click(function(evt) {
|
118
|
+
$(this).data("input").click();
|
119
|
+
evt.preventDefault();
|
120
|
+
return false;
|
121
|
+
});
|
122
|
+
}
|
123
|
+
add.data("input", input);
|
124
|
+
|
125
|
+
input.trigger("upload:setupcomplete");
|
126
|
+
}
|
127
|
+
|
128
|
+
function replaceInput(inp) {
|
129
|
+
var that = $(inp),
|
130
|
+
clone = that.clone();
|
131
|
+
that.replaceWith(clone);
|
132
|
+
clone.trigger("upload:enable");
|
133
|
+
}
|
134
|
+
|
135
|
+
function handleSubmit(evt) {
|
136
|
+
evt.preventDefault();
|
137
|
+
return false;
|
138
|
+
}
|
139
|
+
|
140
|
+
function setupEach(evt) {
|
141
|
+
$(this).each(function(i, el) {
|
142
|
+
setupFileField(el);
|
143
|
+
});
|
144
|
+
}
|
145
|
+
|
146
|
+
$('body').on("upload:enable", ":file[data-file-upload]", setupEach);
|
147
|
+
$(":file[data-file-upload]").trigger("upload:enable");
|
148
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
10
|
+
*
|
11
|
+
*= require_self
|
12
|
+
*= require_tree .
|
13
|
+
*/
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class FileUpload::RedisFilesController < ApplicationController
|
2
|
+
|
3
|
+
def index
|
4
|
+
end
|
5
|
+
|
6
|
+
def new
|
7
|
+
@redis_file = FileUpload::RedisFile.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
@redis_file = FileUpload::RedisFile.new(params[:redis_file])
|
12
|
+
if @redis_file.save
|
13
|
+
redirect_to @redis_file
|
14
|
+
else
|
15
|
+
render :new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def show
|
20
|
+
@redis_file = FileUpload::RedisFile.find(params[:id])
|
21
|
+
end
|
22
|
+
|
23
|
+
def preview
|
24
|
+
@redis_file = FileUpload::RedisFile.find(params[:id])
|
25
|
+
send_data @redis_file.data, type: @redis_file.type, disposition: "inline"
|
26
|
+
end
|
27
|
+
|
28
|
+
def destroy
|
29
|
+
@redis_file = FileUpload::RedisFile.find(params[:id])
|
30
|
+
@redis_file.destroy
|
31
|
+
end
|
32
|
+
|
33
|
+
def bulk
|
34
|
+
unless params[:redis_files]
|
35
|
+
render status: 400, json: { files: [{error: 'No files uploaded.'}] }
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
files = Array(params.require(:redis_files)).map do |file|
|
40
|
+
f = FileUpload::RedisFile.new({
|
41
|
+
file_io: file[:data]
|
42
|
+
})
|
43
|
+
f.save
|
44
|
+
f
|
45
|
+
end
|
46
|
+
respond_to do |format|
|
47
|
+
format.js {
|
48
|
+
render json: {
|
49
|
+
files: files.map{|file| file.to_json}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FileUpload
|
2
|
+
module TagHelper
|
3
|
+
def multiple_file_upload_field(model_name, attribute_name, value, options = {}, &block)
|
4
|
+
render(layout: "file_upload/tags/multiple", locals: {
|
5
|
+
model_name: model_name,
|
6
|
+
attribute_name: attribute_name,
|
7
|
+
value: value,
|
8
|
+
options: options
|
9
|
+
}, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def single_file_upload_field(model_name, attribute_name, value, options = {}, &block)
|
13
|
+
render(layout: "file_upload/tags/single", locals: {
|
14
|
+
model_name: model_name,
|
15
|
+
attribute_name: attribute_name,
|
16
|
+
value: value,
|
17
|
+
options: options
|
18
|
+
}, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def file_size(file)
|
22
|
+
number_with_precision(file.size / 1024.0, precision: 2)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module FileUpload
|
2
|
+
module BuilderMixin
|
3
|
+
|
4
|
+
def multiple_file_upload(field, options = {}, &block)
|
5
|
+
@template.multiple_file_upload_field(@object_name, field, @object.send(field).to_a, options, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def single_file_upload(field, options = {}, &block)
|
9
|
+
@template.single_file_upload_field(@object_name, field, @object.send(field), options, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module FileUpload
|
2
|
+
class RedisFile
|
3
|
+
extend ActiveModel::Naming
|
4
|
+
|
5
|
+
# 1 hour
|
6
|
+
FILE_EXPIRY = 60 * 60
|
7
|
+
|
8
|
+
attr_accessor :file_io
|
9
|
+
attr_writer :key, :name, :id
|
10
|
+
|
11
|
+
def initialize(attrs = {})
|
12
|
+
attrs.each do |k, v|
|
13
|
+
send("#{k}=", v) if respond_to?("#{k}=")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
def find_by_id(uuid)
|
20
|
+
key = base_key(uuid)
|
21
|
+
name, type, size = redis.mget(name_key(key), type_key(key), size_key(key))
|
22
|
+
if name && type && size
|
23
|
+
new({
|
24
|
+
id: uuid,
|
25
|
+
key: key,
|
26
|
+
name: name,
|
27
|
+
type: type,
|
28
|
+
size: size
|
29
|
+
})
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def find(uuid)
|
36
|
+
find_by_id(uuid) || raise(ActiveRecord::RecordNotFound)
|
37
|
+
end
|
38
|
+
|
39
|
+
def name_key(base_key)
|
40
|
+
"#{base_key}:name"
|
41
|
+
end
|
42
|
+
|
43
|
+
def data_key(base_key)
|
44
|
+
"#{base_key}:data"
|
45
|
+
end
|
46
|
+
|
47
|
+
def type_key(base_key)
|
48
|
+
"#{base_key}:type"
|
49
|
+
end
|
50
|
+
|
51
|
+
def size_key(base_key)
|
52
|
+
"#{base_key}:size"
|
53
|
+
end
|
54
|
+
|
55
|
+
def base_key(uuid)
|
56
|
+
"redis_file:#{uuid}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def key
|
61
|
+
@key ||= self.class.base_key(id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def id
|
65
|
+
@id ||= SecureRandom.random_number(1000000000)
|
66
|
+
end
|
67
|
+
|
68
|
+
def data_key
|
69
|
+
self.class.data_key(key)
|
70
|
+
end
|
71
|
+
|
72
|
+
def name_key
|
73
|
+
self.class.name_key(key)
|
74
|
+
end
|
75
|
+
|
76
|
+
def type_key
|
77
|
+
self.class.type_key(key)
|
78
|
+
end
|
79
|
+
|
80
|
+
def size_key
|
81
|
+
self.class.size_key(key)
|
82
|
+
end
|
83
|
+
|
84
|
+
def name
|
85
|
+
@name ||= begin
|
86
|
+
if file_io
|
87
|
+
file_io.original_filename
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def data
|
95
|
+
@data ||= begin
|
96
|
+
if file_io
|
97
|
+
file_io.read
|
98
|
+
elsif key
|
99
|
+
redis.get(data_key)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def data=(data)
|
105
|
+
@data = data
|
106
|
+
end
|
107
|
+
|
108
|
+
def size
|
109
|
+
@size ||= data.size
|
110
|
+
end
|
111
|
+
|
112
|
+
def type
|
113
|
+
@type ||= FileMagic.new(FileMagic::MAGIC_MIME_TYPE).buffer(data)
|
114
|
+
end
|
115
|
+
|
116
|
+
def save
|
117
|
+
redis.setex(data_key, FILE_EXPIRY, data)
|
118
|
+
redis.setex(name_key, FILE_EXPIRY, name)
|
119
|
+
redis.setex(type_key, FILE_EXPIRY, type)
|
120
|
+
redis.setex(size_key, FILE_EXPIRY, size)
|
121
|
+
end
|
122
|
+
|
123
|
+
def destroy
|
124
|
+
redis.del(data_key, name_key, type_key, size_key)
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_json
|
128
|
+
{
|
129
|
+
size: size,
|
130
|
+
name: name,
|
131
|
+
type: type,
|
132
|
+
id: id,
|
133
|
+
key: key
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
def to_key
|
138
|
+
[key]
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_param
|
142
|
+
id.to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
def persisted?
|
146
|
+
redis.exists(data_key)
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
def redis
|
152
|
+
self.class.redis
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.redis
|
156
|
+
@redis ||= FileUpload::Engine.config.redis
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FileUpload
|
2
|
+
module RedisFileReadable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
attr_reader :key
|
7
|
+
attr_accessor :copy_from
|
8
|
+
end
|
9
|
+
|
10
|
+
def key=(key)
|
11
|
+
@key = key
|
12
|
+
if redis_file = RedisFile.find(key)
|
13
|
+
self.data = redis_file.data
|
14
|
+
self.name = redis_file.name
|
15
|
+
self.content_type = redis_file.type
|
16
|
+
self.size = redis_file.size
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module FileUpload
|
2
|
+
class TempFile
|
3
|
+
attr_accessor :name, :content_type, :data, :size
|
4
|
+
|
5
|
+
include FileUpload::RedisFileReadable
|
6
|
+
|
7
|
+
def initialize(attributes = {})
|
8
|
+
attributes.each do |k, v|
|
9
|
+
self.send("#{k}=", v) if respond_to?("#{k}=")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def persisted?
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Redis Files</h1>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h1>Redis File</h1>
|
2
|
+
|
3
|
+
<dl>
|
4
|
+
<dt>Key:</dt>
|
5
|
+
<dd><%= @redis_file.key %></dd>
|
6
|
+
|
7
|
+
<dt>Name:</dt>
|
8
|
+
<dd><%= @redis_file.name %></dd>
|
9
|
+
|
10
|
+
<dt>Size:</dt>
|
11
|
+
<dd><%= @redis_file.size %></dd>
|
12
|
+
|
13
|
+
<dt>Type:</dt>
|
14
|
+
<dd><%= @redis_file.type %></dd>
|
15
|
+
</dl>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
<%
|
2
|
+
# model_name
|
3
|
+
# attribute_name
|
4
|
+
# value
|
5
|
+
# options
|
6
|
+
preview = options.delete(:preview) || false
|
7
|
+
hide_files = options.delete(:hide_files) || false
|
8
|
+
prefix = "#{model_name}[#{attribute_name}_attributes]"
|
9
|
+
file_attrs = {
|
10
|
+
id: "#{model_name}_#{attribute_name}_file",
|
11
|
+
data: {
|
12
|
+
:"model-name" => model_name,
|
13
|
+
:"attr-name" => attribute_name,
|
14
|
+
:"file-upload" => file_upload.bulk_redis_files_path
|
15
|
+
},
|
16
|
+
:multiple => true
|
17
|
+
}.tap do |a|
|
18
|
+
a[:"data-autosubmit"] = true if options.delete(:autosubmit)
|
19
|
+
end
|
20
|
+
-%>
|
21
|
+
<div class="file_upload <%= attribute_name %>">
|
22
|
+
<ul class="unstyled<%= ' hide' if hide_files %>">
|
23
|
+
<% Array(value).each do |file| -%>
|
24
|
+
<li><label>
|
25
|
+
<% if file.persisted? %>
|
26
|
+
<%= hidden_field_tag("#{prefix}[#{file.id}][id]", file.id) %>
|
27
|
+
<%= hidden_field_tag("#{prefix}[#{file.id}][_destroy]", 1) %>
|
28
|
+
<%= check_box_tag("#{prefix}[#{file.id}][_destroy]", 0, checked: true) %>
|
29
|
+
<% elsif file.key %>
|
30
|
+
<%= check_box_tag("#{prefix}[#{SecureRandom.random_number(1000000000)}][key]", file.key, checked: true) %>
|
31
|
+
<% elsif file.copy_from %>
|
32
|
+
<%= check_box_tag("#{prefix}[#{SecureRandom.random_number(1000000000)}][copy_from]", file.copy_from, checked: true) %>
|
33
|
+
<% end %>
|
34
|
+
<%= file.name %> (<%= file_size file %>)kB
|
35
|
+
</label></li>
|
36
|
+
<% end -%>
|
37
|
+
</ul>
|
38
|
+
<% if preview %><p class="preview"><%= image_tag(value.url) if value && value.persisted? %></p><% end %>
|
39
|
+
<div class="errors"></div>
|
40
|
+
<div class="hide">
|
41
|
+
<%= file_field_tag "redis_files[][data]", file_attrs %>
|
42
|
+
</div>
|
43
|
+
<%= yield %>
|
44
|
+
</div>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<%
|
2
|
+
# model_name
|
3
|
+
# attribute_name
|
4
|
+
# value
|
5
|
+
# options
|
6
|
+
preview = options.delete(:preview) || false
|
7
|
+
hide_files = options.delete(:hide_files) || false
|
8
|
+
prefix = "#{model_name}[#{attribute_name}_attributes]"
|
9
|
+
file_attrs = {
|
10
|
+
id: "#{model_name}_#{attribute_name}_file",
|
11
|
+
data: {
|
12
|
+
:"model-name" => model_name,
|
13
|
+
:"attr-name" => attribute_name,
|
14
|
+
:"file-upload" => file_upload.bulk_redis_files_path
|
15
|
+
}
|
16
|
+
}.tap do |a|
|
17
|
+
a[:"data-autosubmit"] = true if options.delete(:autosubmit)
|
18
|
+
end
|
19
|
+
-%>
|
20
|
+
<div class="file_upload <%= attribute_name %>">
|
21
|
+
<% if value && value.persisted? %>
|
22
|
+
<%= hidden_field_tag("#{prefix}[id]", value.id) %>
|
23
|
+
<% end %>
|
24
|
+
<%= hidden_field_tag("#{prefix}[_destroy]", 1) %>
|
25
|
+
<div class="inputs <%= 'hide' if hide_files %>">
|
26
|
+
<% if value %>
|
27
|
+
<label>
|
28
|
+
<% if value.persisted? %>
|
29
|
+
<%= check_box_tag("#{prefix}[_destroy]", 0, checked: true) %>
|
30
|
+
<% elsif value.key %>
|
31
|
+
<%= hidden_field_tag("#{prefix}[key]", value.key) %>
|
32
|
+
<%= check_box_tag("#{prefix}[_destroy]", 0, checked: true) %>
|
33
|
+
<% elsif value.copy_from %>
|
34
|
+
<%= check_box_tag("#{prefix}][copy_from]", file.copy_from, checked: true) %>
|
35
|
+
<% end %>
|
36
|
+
<%= value.name %> (<%= file_size value %>)kB
|
37
|
+
</label>
|
38
|
+
<% end %>
|
39
|
+
</div>
|
40
|
+
<% if preview %><p class="preview"><%= image_tag(value.url) if value && value.url %></p><% end %>
|
41
|
+
<div class="errors"></div>
|
42
|
+
<div class="hide">
|
43
|
+
<%= file_field_tag "redis_files[][data]", file_attrs %>
|
44
|
+
</div>
|
45
|
+
<%= yield %>
|
46
|
+
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>FileUpload</title>
|
5
|
+
<%= stylesheet_link_tag "file_upload/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "file_upload/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|