Skip to content

Instantly share code, notes, and snippets.

@shun115
Last active December 4, 2017 11:07
Show Gist options
  • Save shun115/6ed0f4d9ccfee00f806167c30224056f to your computer and use it in GitHub Desktop.
Save shun115/6ed0f4d9ccfee00f806167c30224056f to your computer and use it in GitHub Desktop.
nginx + nginx-upload-module + jQuery-File-Upload + railsでchunk upload
# 参考: http://bclennox.com/extremely-large-file-uploads-with-nginx-passenger-rails-and-jquery
cd lib
git clone https://github.com/vkholodkov/nginx-upload-module.git
cd nginx-upload-module
# 新し目のnginxに対応したブランチに変更
git checkout 2.255
# nginx-upload-moduleでstateファイルにアクセスできないバグに対応するパッチを当てる
# https://github.com/vkholodkov/nginx-upload-module/issues/41#issuecomment-59182124
--- ngx_http_upload_module.c.bak 2017-11-30 20:57:38.586963921 +0900
+++ ngx_http_upload_module.c 2017-11-30 21:02:49.299370085 +0900
@@ -1195,7 +1195,7 @@
ngx_file_t *file = &u->output_file;
ngx_path_t *path = u->store_path;
- ngx_path_t *state_path = u->state_store_path;
+// ngx_path_t *state_path = u->state_store_path;
uint32_t n;
ngx_uint_t i;
ngx_int_t rc;
@@ -1243,17 +1243,20 @@
return NGX_UPLOAD_NOMEM;
}
- state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state")-1;
+// state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state")-1;
+ state_file->name.len = file->name.len + 1 + sizeof(".state");
state_file->name.data = ngx_palloc(u->request->pool, state_file->name.len + 1);
if(state_file->name.data == NULL)
return NGX_UPLOAD_NOMEM;
- ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
- (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
- "%V.state%Z", &u->session_id);
-
- ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
+// ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
+// (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
+// "%V.state%Z", &u->session_id);
+//
+// ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
+ ngx_memcpy(state_file->name.data, file->name.data, file->name.len);
+ ngx_memcpy(state_file->name.data + file->name.len, ".state", sizeof(".state") + 1);
ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
"hashed path of state file: %s", state_file->name.data);
# 適用
patch -u ngx_http_upload_module.c < patch
# nginxコンパイル
sudo /usr/local/rvm/bin/rvm all do passenger-install-nginx-module --auto --auto-download --prefix=/opt/nginx --extra-configure-flags=" \
--conf-path=/etc/nginx/nginx.conf \
--pid-path=/var/run/nginx.pid \
--sbin-path=/usr/sbin \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gunzip_module \
--with-http_ssl_module \
--add-module='/root/lib/nginx-upload-module'"
# nginx.conf設定の導入
if ($args ~ chunk_movie_upload=on ) {
set $upload_field_name "user_post[movie]";
upload_pass @rails;
upload_pass_form_field ".*";
upload_store /tmp;
upload_store_access user:rw group:rw all:rw;
upload_resumable on;
upload_set_form_field "$upload_field_name[original_filename]" "$upload_file_name";
upload_set_form_field "$upload_field_name[content_type]" "$upload_content_type";
upload_set_form_field "$upload_field_name[file_path]" "$upload_tmp_path";
# upload_aggregate_form_field "$upload_field_name[md5]" "$upload_file_md5";
upload_aggregate_form_field "$upload_field_name[size]" "$upload_file_size";
# upload_cleanup 200-599;
}
# JS
var hash, sessionId;
hash = function(s, tableSize) {
var a, b, h, i, j, ref;
b = 27183;
h = 0;
a = 31415;
for (i = j = 0, ref = s.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
h = (a * h + s[i].charCodeAt()) % tableSize;
a = ((a % tableSize) * (b % tableSize)) % tableSize;
}
return h;
};
sessionId = function(filename) {
return hash(filename, 16384);
};
var url = "<%= hoge_path(chunk_movie_upload: 'on') %>";
$('#file-addmovie').fileupload({
url: url,
dataType: 'text',
type: 'POST',
<% if Rails.env == 'staging' || Rails.env == 'production' -%>
maxChunkSize: 1*1024*1024,
multipart: false,
<% else -%>
multipart: true,
<% end -%>
paramName: 'user_post[movie]',
add: function(e, data) {
data.headers || (data.headers = {});
data.headers['Session-Id'] = sessionId(data.files[0].name);
return data.submit();
},
send: function(e, data){
$('#progress').text('');
$('#movies > .wrap-movie').remove();
loading.show();
},
done: function (e, data) {
loading.hide();
$('#movies').append('<li class="wrap-movie"><div class="img"><video src="' + JSON.parse(data.result)[0].url + '" width="200" controls></video></div></li>');
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
$('#progress').text(progress + '%');
}
});
# rails
movie_param = params[:user_post][:movie]
if movie_param.class.name == 'ActionController::Parameters' && movie_param[:file_path].present?
required_params[:movie] = ActionDispatch::Http::UploadedFile.new(
filename: movie_param[:original_filename],
type: movie_param[:content_type],
tempfile: File.new(movie_param[:file_path]),
head: nil
)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment