http://www.grid.net.ru/nginx/resumable_uploads.en.html
老板写的可以运行的程序
require 'rubygems' require 'curb' require 'digest/sha1' require 'base64' c= Curl::Easy.new file = ARGV[0] session_id = ARGV[1] upload_uri = "http://xxxxx:xxxxx/upload" #check if file is valid, get file info # if File.exist?(file) and File.ftype(file) == "file" then file_size = File.size(file) file_name = File.basename(file) end #upload file now using range params #first cut file into 50k byte (51200) slice, then send each slice using resume # # we accept file max size 500M now. should we support larger file, change -a slice_block = 51200; prefix = "chunk#{session_id}_" `split -b #{slice_block} -d -a 4 #{file} #{prefix}` #shell to split files if($?.to_i != 0) then puts "fail to split file, command as split -b #{slice_block} -d -a 4 #{file} #{prefix}`" exit end file_chunks = `ls chunk#{session_id}_*`.split("\n") file_chunks.each { |chunk| c.url = upload_uri c.multipart_form_post = false chunk =~ /^chunk(\d+)_(\d+)/ chunk_id = $2 chunk_range_start = $2.to_i * 51200; chunk_range_stop = ($2.to_i+1) *51200<File.size(file)?($2.to_i+1)*51200:File.size(file) puts "uploading chunk_range : #{chunk_range_start} ---> #{chunk_range_stop-1}" c.headers["Session-ID"] = session_id c.headers["X-Content-Range"] = "bytes #{chunk_range_start}-#{chunk_range_stop-1}/#{file_size}" c.headers["Content-Disposition"] = 'attachment; filename="'+ file_name+'"' c.headers["Content-Type"] = "application/octet-stream" File.open(chunk, 'r') do|image_file| c.http_post(image_file.read) end puts "response is #{c.response_code}" puts c.body_str } #cleanup file chunks `rm -f chunk*`
---------------------------2012-6-19---------------------------------------------------------------------------
对以上代码的修改
# -*- encoding: utf-8 -*- # Test file upload process # first: split file to chunk files # second: send file to server # third: clean chunk files require 'rubygems' require 'action_view' require 'curb' include ActionView::Helpers::NumberHelper module FileUpload class File #init File class # file_path: file path # session_id: session_id for upload module, like id tag # slice_block: chunk size of file # open_uri: remote url def initialize(file_path, session_id, open_uri = 'http://xxxxx:xxx/upload_video', slice_block = 51200) raise Exception, "File was not exsit!" unless ::File.file? file_path @c = Curl::Easy.new @file_path = file_path @session_id = session_id @file_size = ::File.size(file_path) @file_name = ::File.basename(file_path) @slice_block = slice_block @open_uri = open_uri end def upload_file_to_nginx_module split begin upload ensure clean end end private #first cut file into 50k byte (51200) slice, then send each slice using resume # # we accept file max size 500M now. should we support larger file, change -a def split prefix = "chunk#{@session_id}_" cmd = "split -b #{@slice_block} -d -a 4 #{@file_path} #{prefix}" puts "cmd is #{cmd}" `#{cmd}` #shell to split files raise Exception, "fail to split file, command as split -b #{@slice_block} -d -a 4 #{@file_file} #{prefix}" if $?.to_i != 0 end #upload file def upload file_chunks = `ls chunk#{@session_id}_*`.split("\n") file_chunks.each { |chunk| @c.url = @open_uri @c.multipart_form_post = false chunk =~ /^chunk(\d+)_(\d+)/ chunk_id = $2 chunk_range_start = $2.to_i * @slice_block; chunk_range_stop = ($2.to_i+1) * @slice_block < @file_size ? ($2.to_i+1) * @slice_block : @file_size @c.headers["Session-ID"] = @session_id @c.headers["X-Content-Range"] = "bytes #{chunk_range_start}-#{chunk_range_stop-1}/#{@file_size}" @c.headers["Content-Disposition"] = 'attachment; filename="'+ @file_name+'"' @c.headers["Content-Type"] = "application/octet-stream" ::File.open(chunk, 'r') do|file| @c.http_post(file.read) end puts "response is #{@c.response_code}" puts @c.body_str } end #cleanup file chunks def clean `rm -f chunk*` end end end #image upload class ImageUpload class << self def upload file_path = '1.jpg' session_id = 1.upto(5).inject { |a, i | a << rand(10) } puts "image upload session id is #{session_id}" puts "----------------------------------------" puts "File size: #{number_to_human_size File.size file_path}" start_time = Time.now.to_i open_uri = "http://xxxxx:xxxxx/upload_image?title='test'&phones=13810304030&user_id=1" slice_block = 1024 * 300 @image_upload = FileUpload::File.new(file_path, session_id, open_uri) @image_upload.upload_file_to_nginx_module stop_time = Time.now.to_i seconds = stop_time - start_time puts "Send File Time: #{seconds}" puts "Speed is : #{File.size(file_path) * 8 / seconds }" end end end #ImageUpload.upload #video upload class VideoUpload class << self def upload file_path = '8m.3gp' #'218k.3gp' |'8m.3gp' session_id = 1.upto(5).inject { |a, i | a << rand(10) } puts "video upload session id is #{session_id}" puts "----------------------------------------" puts "File size: #{number_to_human_size File.size file_path}" start_time = Time.now.to_i slice_block = 1024 * 300 open_uri = "http://xxxxxx:xxxxxx/upload_video?title='test'&phones=13810304030&user_id=1" @video_upload = FileUpload::File.new(file_path, session_id, open_uri, slice_block) @video_upload.upload_file_to_nginx_module stop_time = Time.now.to_i time = stop_time - start_time puts "Send File Time: #{time} s " puts "Speed is : #{File.size(file_path) * 8 / time}" end end end VideoUpload.upload
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib') require 'test/unit' require 'file_upload' class FileUploadTest < Test::Unit::TestCase def setup @default_slice_block = 51200 @default_session_id = '123456789' @file_path = '1.jpg' @file_upload = FileUpload::File.new(@file_path, @default_session_id) end def test_split_and_clean file_size = File.size @file_path num = file_size / @default_slice_block @file_upload.send(:split) assert_equal num + 1, Dir.entries(".").select { |file| file =~ /chunk*/ }.count @file_upload.send(:clean) assert_equal 0 ,Dir.entries(".").select { |file| file =~ /chunk*/ }.count end end