nvx-sds 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -25,8 +25,9 @@
25
25
  #When you are finished with a session always be sure to log that session out to invalidate the active session token.
26
26
  #
27
27
  #session.Logout
28
+ #
28
29
  #==== Uploading a file
29
- #To upload a file you will need to create a session and a SoapUpload object. The call takes a local file and streams it to the remote server. Below is an example of an upload:
30
+ #To upload a file you will need to create a session and an HttpUpload object. The call takes a local file and streams it to the remote server. Below is an example of an upload:
30
31
  #
31
32
  #session = Session.new("APP-KEY", "USERNAME", "APP NAME", "PASSWORD")
32
33
  #
@@ -42,6 +43,19 @@
42
43
  #
43
44
  #HttpUpload.UploadFile(remote_path, remote_file, local_file, overwrite, session.account_login)
44
45
  #
46
+ #==== Downloading a file
47
+ #To download a file you will need to create a session and a HttpDownload object. The call creates a link to a remote Nirvanix file and downloads it to a local file. Below is an example of a download:
48
+ #
49
+ #session = Session.new("APP-KEY", "USERNAME", "APP NAME", "PASSWORD")
50
+ #
51
+ #remote_file = "/TestUpload.txt"
52
+ #
53
+ ##Get a file in the current directory
54
+ #
55
+ #local_file = File.expand_path(File.join(File.dirname(__FILE__), '.')) + remote_file
56
+ #
57
+ #HttpDownload.DownloadFile(remote_path, local_file, session.account_login)
58
+ #
45
59
  #==== General File operations
46
60
  #There are many different file operations that are available once a file has been uploaded through the API. To get to these operations you can retrieve the root folder. The example below shows this and prints out each folder name.
47
61
  #
data/RELEASENOTES CHANGED
@@ -1,6 +1,12 @@
1
1
  #= NVX::SDS
2
2
  #== A Ruby SDK for Nirvanix
3
3
  #=== Release Notes
4
+ #==== May 21, 2009 - 1.3.0
5
+ #
6
+ # * Modified the HttpUpload class to provide more robust retry logic. Also added callback methods to the HttpUpload.UploadFile method (See RDoc for details).
7
+ # * Created an HttpDownload class to allow downloads with retry logic and callbacks for status and percent complete. This also downloads the files in chunks and should handle huge files without issues.
8
+ # * Corrected a number of RDoc issues throughout the code.
9
+ #
4
10
  #==== May 19, 2009 - 1.2.1
5
11
  #
6
12
  # * Corrected upload problem with multi-part files in the HttpUpload class.
@@ -0,0 +1,138 @@
1
+ # = Overview
2
+ #
3
+ # The HttpUpload is used to upload files via HTTP POST. This class can upload a maximum of 256gb.
4
+ module NVX
5
+ module SDS
6
+ $:.unshift File.dirname(__FILE__)
7
+ require 'apiparam'
8
+ require 'apicommand'
9
+ require 'apiclasses/responsecodes'
10
+ require 'net/https'
11
+ require 'net/http'
12
+ require 'REXML/Document'
13
+ require 'uri'
14
+
15
+ # = Overview
16
+ #
17
+ # This exception is raised if a transfer fails after retrying 10 times.
18
+ class RetryException < RuntimeError
19
+ attr :error_message
20
+ def initialize(error_message)
21
+ @error_message = error_message
22
+ end
23
+ end
24
+
25
+ # = Overview
26
+ # Retrieves a file from the remote server and saves it to the local file system.
27
+ class HttpDownload
28
+ USERAGENT = "Nirvanix Ruby SDK"
29
+ Newline = "\r\n" unless const_defined? "Newline"
30
+ BLOCKSIZE = 1024*1024 * 4
31
+
32
+ # = Overview
33
+ # Retrieves a file from the remote server and saves it to the local file system
34
+ # The path can be relative or absolute, if you are a master account you can pass the absolute path
35
+ # to retrieve a child accounts file.
36
+ #
37
+ # = Parameters
38
+ # == path
39
+ # A path on the Nirvanix file system this can be absolute or regular
40
+ # == local_path
41
+ # The local path where the file will be saved
42
+ # == account_login
43
+ # the account information from the Session.new that was created with valid login information.
44
+ # == callback
45
+ # The callback is an object with two methods for recieving updates while the download is in progress.
46
+ #
47
+ # def percent(percent, bytes_downloaded)
48
+ # print "Percent: #{percent} - Bytes: #{bytes_downloaded}\r\n"
49
+ # end
50
+ #
51
+ # Returns a the percentage complete along with the number of bytes downloaded so far.
52
+ #
53
+ # def warning(message)
54
+ # print "Warning: #{message}\r\n"
55
+ # end
56
+ #
57
+ # Returns a warning message when there is an internal connectivity issue with the download.
58
+ #
59
+ def HttpDownload.DownloadFile(path, local_path, account_login, callback = nil)
60
+ # Get the download node this will get the proper node for a path.
61
+ doc = Transport.execute_command_post(APICommand.GetOptimalURLs, [APIParam.new("filePath", path), APIParam.new("expiration", "#{60*60*24}")], account_login)
62
+ url = URI.parse(doc.root.elements["//Response/Download/DownloadURL"].get_text.value)
63
+
64
+ file = open(local_path, "wb")
65
+ offset = 0
66
+ percent_downloaded = 0
67
+ total_length = 1
68
+ while offset < total_length
69
+ begin
70
+ # download a portion of the file up to blocksize long.
71
+ response = DownloadChunk(url.host, url.path, offset, BLOCKSIZE, false)
72
+ # if this is the first request get the total file length based on the Content-Range.
73
+ if offset == 0
74
+ range = response['Content-Range']
75
+ total_length = range[range.index("/") + 1, range.length].to_i
76
+ end
77
+ # reset retry_count since this was a successful download.
78
+ retry_count = 0
79
+ rescue SocketError, Timeout::Error, Errno::ETIMEDOUT, Errno::EPIPE, IOError, RetryException
80
+ retry_count += 1
81
+ if retry_count > 10
82
+ raise RetryException.new("Connection failure at offset: #{offset}")
83
+ end
84
+
85
+ # If the callback object is available pass a warning specifying the retry count.
86
+ if !callback.nil?
87
+ callback.warning("Connection failure at offset: #{offset} Retry count: #{retry_count}")
88
+ end
89
+
90
+ redo
91
+ end
92
+ # write the body to the open file.
93
+ file.write(response.body)
94
+ # increment the offset by how much was downloaded.
95
+ offset += response.body.length
96
+
97
+ # get percentage complete
98
+ new_percentage = (offset * 100) / total_length
99
+ # if the percentage has changed update the callback.
100
+ if new_percentage > percent_downloaded
101
+ percent_downloaded = new_percentage
102
+ if !callback.nil?
103
+ callback.percent(percent_downloaded, offset)
104
+ end
105
+ end
106
+ end
107
+ file.close
108
+ end
109
+
110
+ # = Overview
111
+ # Downloads a single chunk from a remote server.
112
+ def HttpDownload.DownloadChunk(node, path, offset, length, is_secure)
113
+
114
+ port = is_secure ? 443 : 80
115
+
116
+ # Create the first http(s) object.
117
+ if @h.nil?
118
+ @h = Net::HTTP.new(node, port)
119
+ end
120
+
121
+ # if you are just testing and do not have a SSL certificate setup properly you can
122
+ # disable warnings with the below line. Just uncomment to ignore SSL session warnings.
123
+ @h.verify_mode = OpenSSL::SSL::VERIFY_NONE if is_secure
124
+
125
+ # put the SSL in the correct mode based on the apicommand
126
+ @h.use_ssl = is_secure
127
+ headers = {
128
+ 'Range' => "bytes=#{offset}-#{length + offset}",
129
+ 'User-Agent' => USERAGENT,
130
+ 'Content-Length' => "4"
131
+ }
132
+
133
+ # do the command and return the response.
134
+ return @h.post(path, "\r\n\r\n", headers)
135
+ end
136
+ end
137
+ end
138
+ end
@@ -1,34 +1,71 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+ require 'apiparam'
3
+ require 'apicommand'
4
+ require 'apiclasses/responsecodes'
5
+ require 'net/https'
6
+ require 'net/http'
7
+ require 'REXML/Document'
8
+ require 'uri'
9
+
10
+
1
11
  # = Overview
2
12
  #
3
13
  # The HttpUpload is used to upload files via HTTP POST. This class can upload a maximum of 256gb.
4
14
  module NVX
5
15
  module SDS
6
16
 
7
- $:.unshift File.dirname(__FILE__)
8
- require 'apiparam'
9
- require 'apicommand'
10
- require 'apiclasses/responsecodes'
11
- require 'net/https'
12
- require 'net/http'
13
- require 'REXML/Document'
14
- require 'uri'
15
-
16
17
  USERAGENT = "Nirvanix Ruby SDK"
17
18
  Newline = "\r\n" unless const_defined? "Newline"
18
19
  # upload 4 Mb chunks. 4 Mb is usually quite efficient but you may want to change this
19
20
  # to a larger value if you are on a very fast connection or have very high latency.
20
21
  BLOCKSIZE = 1024 * 1024 * 4
21
22
 
23
+ # = Overview
24
+ #
25
+ # This exception is raised if a transfer fails after retrying 10 times.
22
26
  class RetryException < RuntimeError
23
-
27
+ attr :error_message
28
+ def initialize(error_message)
29
+ @error_message = error_message
30
+ end
24
31
  end
25
32
 
26
33
  # = Overview
27
34
  #
28
- # The HttpUpload is used to upload files via HTTP POST. This class can upload a maximum of 256gb.
35
+ # The HttpUpload is used to upload files via HTTP POST. This class can upload a maximum of 256gb.
29
36
  class HttpUpload
30
- # Uploads a file to a destination and allows you to call overwrite.
31
- def HttpUpload.UploadFile(destination_path, destination_filename, local_path, overwrite, account_login)
37
+ # = Overview
38
+ #
39
+ # The HttpUpload.UploadFile is used to upload files via HTTP POST. This class can upload a maximum of 256gb.
40
+ #
41
+ # = Parameters
42
+ # == destination_path
43
+ # The path at Nirvanix where the file will be uploaded
44
+ # == destination_filename
45
+ # The remote filename
46
+ # == local_path
47
+ # The path to the file that will be uploaded on the local file system.
48
+ # == overwrite
49
+ # Determines if the upload should overwrite the file at Nirvanix if it exists.
50
+ # == account_login
51
+ # the account information from the Session.new that was created with valid login information.
52
+ # == callback
53
+ # The callback is an object with two methods for recieving updates while the download is in progress.
54
+ #
55
+ # def percent(percent, bytes_uploaded)
56
+ # print "Percent: #{percent} - Bytes: #{bytes_uploaded}\r\n"
57
+ # end
58
+ #
59
+ # Returns a the percentage complete along with the number of bytes uploaded so far.
60
+ #
61
+ # def warning(message)
62
+ # print "Warning: #{message}\r\n"
63
+ # end
64
+ #
65
+ # Returns a warning message when there is an internal connectivity issue with the upload.
66
+ #
67
+ def HttpUpload.UploadFile(destination_path, destination_filename, local_path, overwrite, account_login,
68
+ callback = nil)
32
69
  # Get the Upload node passing in the total file size.
33
70
  file_size = File.size(local_path)
34
71
  params = Array.new
@@ -48,30 +85,46 @@ module NVX
48
85
  offset = 0
49
86
  retry_count = 0
50
87
  path = "/upload.ashx?uploadToken=#{upload_token}&destFolderPath=#{destination_path}"
88
+ percent_uploaded = 0
51
89
  # Loop through the entire file uploading each file part.
52
90
  while !file.eof?
53
91
  # read a chunk of data from the file.
54
92
  file_data = file.read(BLOCKSIZE)
55
93
  # Send a chunk to Nirvanix using the upload token and node.
56
- retry_chunk = false
57
94
  begin
58
95
  tmppath = path
96
+ # adjust the path to include rangeOverwrite when trying to write to a previous offset.
59
97
  if retry_count > 0
60
98
  tmppath = path + "&rangeOverwrite=true"
61
99
  end
62
100
  params = post_data(node, tmppath, destination_filename, file_data, offset, file_size, false)
63
- rescue RetryException
101
+ # the SystemCallError handles ETIMEOUT, EPIPE, EHOSTDOWN for timeouts and broken pipes.
102
+ rescue SocketError, SystemCallError, IOError, RetryException
103
+ # set the file position to the previous offset to retry that block.
64
104
  file.pos = offset
65
105
  retry_count += 1
66
- retry_chunk = true
106
+ # if the maximum retry count has been reached raise an exception to the outside world.
67
107
  if retry_count == 10
68
- raise RetryException
108
+ raise RetryException.new("Connection failure at offset: #{offset}")
109
+ end
110
+ # If the callback object is available pass a warning specifying the retry count.
111
+ if !callback.nil?
112
+ callback.warning("Connection failure at offset: #{offset} Retry count: #{retry_count}")
69
113
  end
114
+ redo
70
115
  end
116
+ # Reset the retry count to 0 since this was transmission was successful.
117
+ retry_count = 0
71
118
  # advance offset based on how much data was read.
72
- if !retry_chunk
73
- offset += file_data.length
74
- retry_count = 0
119
+ offset += file_data.length
120
+ # get percentage complete
121
+ new_percentage = (offset * 100) / file_size
122
+ # if the percentage has changed update the callback.
123
+ if new_percentage > percent_uploaded
124
+ percent_uploaded = new_percentage
125
+ if !callback.nil?
126
+ callback.percent(percent_uploaded, offset)
127
+ end
75
128
  end
76
129
  end
77
130
  end
@@ -102,21 +155,23 @@ module NVX
102
155
  port = is_secure ? 443 : 80
103
156
 
104
157
  # Create the first http(s) object.
105
- h = Net::HTTP.new(node, port).start
158
+ if @h.nil?
159
+ @h = Net::HTTP.new(node, port).start
160
+ end
106
161
 
107
162
  # if you are just testing and do not have a SSL certificate setup properly you can
108
163
  # disable warnings with the below line. Just uncomment to ignore SSL session warnings.
109
- h.verify_mode = OpenSSL::SSL::VERIFY_NONE if is_secure
164
+ @h.verify_mode = OpenSSL::SSL::VERIFY_NONE if is_secure
110
165
 
111
166
  # put the SSL in the correct mode based on the apicommand
112
- h.use_ssl = is_secure
167
+ @h.use_ssl = is_secure
113
168
 
114
169
  # do the upload and return the response.
115
170
  req = Net::HTTP::Post.new(path, headers)
116
171
  req.content_length = content.length
117
172
  req.content_type = "multipart/form-data; boundary=#{boundary}"
118
173
  req.body = content
119
- response = h.request(req)
174
+ response = @h.request(req)
120
175
 
121
176
  #print "\r\nContent: " + content + "\r\n\r\n"
122
177
  #print "RESPONSE XML: " + response.body + "\r\n\r\n"
@@ -74,9 +74,7 @@ module NVX
74
74
  return doc
75
75
  end
76
76
 
77
- # Retrieves a file from the remote server and saves it to the local file system
78
- # The path can be relative or absolute, if you are a master account you can pass the absolute path
79
- # to retrieve a child accounts file.
77
+ # Obsolete - Use HttpDownload.DownloadFile instead.
80
78
  def Transport.DownloadFile(path, local_path, account_login)
81
79
  # Get the download node this will get the proper node for a path.
82
80
  doc = Transport.execute_command_post(APICommand.GetDownloadNodes, [APIParam.new("filePath", path)], account_login)
data/lib/nvx_sds.rb CHANGED
@@ -18,5 +18,6 @@ require File.join( dir, "masteraccount" )
18
18
  # this will be removed soon as the SOAP upload is obsolete.
19
19
  require File.join( dir, "apiclasses/soapupload" )
20
20
  require File.join( dir, "apiclasses/httpupload" )
21
+ require File.join( dir, "apiclasses/httpdownload" )
21
22
  require File.join( dir, "apiclasses/fsfolderlist" )
22
23
  require File.join( dir, "apiclasses/metadatainfo" )
@@ -0,0 +1,30 @@
1
+
2
+ require 'test/unit'
3
+ $:.unshift File.dirname(__FILE__) + '/../lib/nvx/sds/'
4
+ require 'session'
5
+ require 'REXML/Document'
6
+ require 'httpdownload'
7
+ require File.dirname(__FILE__) + '/testconfig'
8
+ include REXML
9
+
10
+ class Downloadtest < Test::Unit::TestCase
11
+ include NVX::SDS
12
+
13
+ def test_HTTPDownloadFile
14
+ session = Session.new(APP_KEY, USERNAME, APP_NAME, PASSWORD)
15
+
16
+ start_time = Time.now
17
+ local_path = File.expand_path(File.join(File.dirname(__FILE__), '.')) + "/"
18
+ HttpDownload.DownloadFile(DOWNLOADFILE, local_path + "TEMPFILE.TMP", session.account_login, self)
19
+ print "\r\nHttpDownloadFile: #{Time.now - start_time}\r\n"
20
+ end
21
+
22
+ def percent(percent, bytes_downloaded)
23
+ print "Percent: #{percent} - Bytes: #{bytes_downloaded}\r\n"
24
+ end
25
+
26
+ def warning(message)
27
+ print "Warning: #{message}\r\n"
28
+ end
29
+
30
+ end
data/tests/testconfig.rb CHANGED
@@ -16,4 +16,7 @@ MASTERPASSWORD = "MASTERPASSWORD"
16
16
  # Below is the master account information for testing the master account calls.
17
17
  UPLOADPATH = "/"
18
18
  UPLOADFILE = "TestUpload.txt"
19
- UPLOADFILELARGE = "LargeFile.dat"
19
+ UPLOADFILELARGE = "LargeFile.dat"
20
+
21
+ # this file should exist in your Nirvanix account.
22
+ DOWNLOADFILE = "/TestUpload.txt"
data/tests/uploadtest.rb CHANGED
@@ -16,7 +16,7 @@ class Uploadtest < Test::Unit::TestCase
16
16
  start_time = Time.now
17
17
  remote_file = "/TestUpload.txt"
18
18
  local_file = File.expand_path(File.join(File.dirname(__FILE__), '.')) + "/" + UPLOADFILE
19
- #SoapUpload.UploadFile(UPLOADPATH + UPLOADFILE, local_file, session.account_login)
19
+ SoapUpload.UploadFile(UPLOADPATH + UPLOADFILE, local_file, session.account_login)
20
20
  print "\r\nSoapUploadFile: #{Time.now - start_time}\r\n"
21
21
  # remove file after uploading
22
22
  #session.DeleteFiles([UPLOADPATH + UPLOADFILE])
@@ -39,18 +39,27 @@ class Uploadtest < Test::Unit::TestCase
39
39
 
40
40
  start_time = Time.now
41
41
  local_file = File.expand_path(File.join(File.dirname(__FILE__), '.')) + "/" + UPLOADFILE
42
- HttpUpload.UploadFile(UPLOADPATH, UPLOADFILE, local_file, true, session.account_login)
42
+ HttpUpload.UploadFile(UPLOADPATH, UPLOADFILE, local_file, true, session.account_login, self)
43
43
  print "\r\nHttpUploadFile: #{Time.now - start_time}\r\n"
44
44
  # remove file after uploading
45
- session.DeleteFiles([UPLOADPATH + UPLOADFILE])
45
+ #session.DeleteFiles([UPLOADPATH + UPLOADFILE])
46
+ end
47
+
48
+ def percent(percent, bytes_uploaded)
49
+ print "Percent: #{percent} - Bytes: #{bytes_uploaded}\r\n"
46
50
  end
47
51
 
52
+ def warning(message)
53
+ print "Warning: #{message}\r\n"
54
+ end
55
+
48
56
  def test_HTTPUploadFileLarge
49
57
  session = Session.new(APP_KEY, USERNAME, APP_NAME, PASSWORD)
50
58
 
51
59
  start_time = Time.now
52
60
  local_file = File.expand_path(File.join(File.dirname(__FILE__), '.')) + "/" + UPLOADFILELARGE
53
- HttpUpload.UploadFile(UPLOADPATH, UPLOADFILELARGE, local_file, true, session.account_login)
61
+ print "\r\nHttpUploadFileLarge Starting...\r\n"
62
+ HttpUpload.UploadFile(UPLOADPATH, UPLOADFILELARGE, local_file, true, session.account_login, self)
54
63
  print "\r\nHttpUploadFileLarge: #{Time.now - start_time}\r\n"
55
64
  # remove file after uploading
56
65
  session.DeleteFiles([UPLOADPATH + UPLOADFILELARGE])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nvx-sds
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Barry Ruffner
@@ -9,7 +9,7 @@ autorequire: nvx_sds
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-19 00:00:00 -07:00
12
+ date: 2009-05-21 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,6 +31,7 @@ files:
31
31
  - lib/nvx/sds/countries.rb
32
32
  - lib/nvx/sds/folder.rb
33
33
  - lib/nvx/sds/hosteditem.rb
34
+ - lib/nvx/sds/httpdownload.rb
34
35
  - lib/nvx/sds/httpupload.rb
35
36
  - lib/nvx/sds/itembase.rb
36
37
  - lib/nvx/sds/masteraccount.rb
@@ -56,6 +57,7 @@ files:
56
57
  - tests/apicommandtest.rb
57
58
  - tests/apiparamtest.rb
58
59
  - tests/countriestest.rb
60
+ - tests/downloadtest.rb
59
61
  - tests/foldertest.rb
60
62
  - tests/fsfolderlisttest.rb
61
63
  - tests/masteraccounttest.rb
@@ -103,6 +105,7 @@ test_files:
103
105
  - tests/apicommandtest.rb
104
106
  - tests/apiparamtest.rb
105
107
  - tests/countriestest.rb
108
+ - tests/downloadtest.rb
106
109
  - tests/foldertest.rb
107
110
  - tests/fsfolderlisttest.rb
108
111
  - tests/masteraccounttest.rb