filestack 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/README.md +23 -3
- data/VERSION +1 -1
- data/lib/filestack/config.rb +2 -0
- data/lib/filestack/models/filestack_client.rb +11 -9
- data/lib/filestack/ruby/version.rb +1 -1
- data/lib/filestack/utils/multipart_upload_utils.rb +42 -22
- data/lib/filestack/utils/utils.rb +10 -12
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8293c3bf008ad994166882a2029f0936ee64f4c070ef41040d2c97f492f37cb4
|
4
|
+
data.tar.gz: 7749e600bce0dc504a943553ea6e807a5ed4aab8f43813a2cc62fd78d61905ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f44323a268793efb45679967fbeafe880ab3b21299744c5ab545d066bfe72007af5673d4e750e789d400469ae6e3888ba58459f8f5d41713d31d527bdc7f4147
|
7
|
+
data.tar.gz: 01d18cbf962f1548b8cdca6db43d15705d07c15647c638710eaf90d9bba419e315f2fc39ef15ee48bc0e247f4cf7cbcd0bb094525413ded7d4cf341584e90cec
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -55,13 +55,33 @@ filelink = client.upload(filepath: '/path/to/localfile')
|
|
55
55
|
# OR
|
56
56
|
|
57
57
|
filelink = client.upload(external_url: 'http://domain.com/image.png')
|
58
|
+
|
59
|
+
# OR
|
60
|
+
|
61
|
+
file = StringIO.new
|
62
|
+
filelink = client.upload(io: file)
|
58
63
|
```
|
59
64
|
|
60
|
-
To upload a local and an external file with
|
65
|
+
To upload a local, an IO object and an external file with following optional options:
|
66
|
+
|
61
67
|
```ruby
|
62
|
-
|
68
|
+
options = {
|
69
|
+
filename: 'string',
|
70
|
+
location: 'string',
|
71
|
+
path: 'string',
|
72
|
+
container: 'string',
|
73
|
+
mimetype: 'string',
|
74
|
+
region: 'string',
|
75
|
+
workflows: ['workflow-id-1', 'workflow-id-2'],
|
76
|
+
upload_tags: {
|
77
|
+
key: 'value',
|
78
|
+
key2: 'value'
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
filelink = client.upload(filepath: '/path/to/localfile', options: { mimetype: 'image/png', filename: 'custom_filename.png' })
|
63
83
|
|
64
|
-
filelink = client.upload(external_url: 'http://domain.com/image.png', options: { mimetype: 'image/jpeg' })
|
84
|
+
filelink = client.upload(external_url: 'http://domain.com/image.png', options: { mimetype: 'image/jpeg', filename: 'custom_filename.png' })
|
65
85
|
```
|
66
86
|
|
67
87
|
To store file on `dropbox`, `azure`, `gcs` or `rackspace`, you must have the chosen provider configured in the developer portal to enable this feature. By default the file is stored on `s3`. You can add more details of the storage in `options`.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.8.0
|
data/lib/filestack/config.rb
CHANGED
@@ -22,6 +22,8 @@ class FilestackConfig
|
|
22
22
|
'Accept-Encoding' => "application/json"
|
23
23
|
}.freeze
|
24
24
|
|
25
|
+
DEFAULT_UPLOAD_MIMETYPE = 'application/octet-stream'
|
26
|
+
|
25
27
|
INTELLIGENT_ERROR_MESSAGES = ['BACKEND_SERVER', 'BACKEND_NETWORK', 'S3_SERVER', 'S3_NETWORK']
|
26
28
|
|
27
29
|
def self.multipart_start_url
|
@@ -23,23 +23,25 @@ class FilestackClient
|
|
23
23
|
@security = security
|
24
24
|
end
|
25
25
|
|
26
|
-
# Upload a local file or
|
26
|
+
# Upload a local file, external url or IO object
|
27
27
|
# @param [String] filepath The path of a local file
|
28
28
|
# @param [String] external_url An external URL
|
29
|
+
# @param [StringIO] io The IO object
|
29
30
|
# @param [Hash] options User-supplied upload options
|
31
|
+
# @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
|
32
|
+
# @param [String] storage Default storage to be used for uploads
|
30
33
|
#
|
31
34
|
# return [Filestack::FilestackFilelink]
|
32
|
-
def upload(filepath: nil, external_url: nil, options: {}, intelligent: false, timeout: 60, storage: 'S3')
|
33
|
-
return 'You cannot upload a URL and file at the same time' if filepath && external_url
|
35
|
+
def upload(filepath: nil, external_url: nil, io: nil, options: {}, intelligent: false, timeout: 60, storage: 'S3')
|
36
|
+
return 'You cannot upload a URL and file at the same time' if (filepath || io) && external_url
|
34
37
|
|
35
|
-
response = if
|
36
|
-
|
38
|
+
response = if external_url
|
39
|
+
send_upload(@apikey, external_url, @security, options)
|
37
40
|
else
|
38
|
-
|
39
|
-
|
40
|
-
options: options,
|
41
|
-
security: @security)
|
41
|
+
return 'You cannot upload IO object and file at the same time' if io && filepath
|
42
|
+
multipart_upload(@apikey, filepath, io, @security, options, timeout, storage, intelligent)
|
42
43
|
end
|
44
|
+
|
43
45
|
FilestackFilelink.new(response['handle'], security: @security, apikey: @apikey)
|
44
46
|
end
|
45
47
|
# Transform an external URL
|
@@ -13,13 +13,22 @@ include UploadUtils
|
|
13
13
|
include IntelligentUtils
|
14
14
|
# Includes all the utility functions for Filestack multipart uploads
|
15
15
|
module MultipartUploadUtils
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
def get_file_attributes(file, options = {})
|
18
|
+
filename = options[:filename] || File.basename(file)
|
19
|
+
mimetype = options[:mimetype] || MimeMagic.by_magic(File.open(file)) || FilestackConfig::DEFAULT_UPLOAD_MIMETYPE
|
18
20
|
filesize = File.size(file)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
|
22
|
+
[filename, filesize, mimetype.to_s]
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_io_attributes(io, options = {})
|
26
|
+
filename = options[:filename] || 'unnamed_file'
|
27
|
+
mimetype = options[:mimetype] || FilestackConfig::DEFAULT_UPLOAD_MIMETYPE
|
28
|
+
|
29
|
+
io.seek(0, IO::SEEK_END)
|
30
|
+
filesize = io.tell
|
31
|
+
|
23
32
|
[filename, filesize, mimetype.to_s]
|
24
33
|
end
|
25
34
|
|
@@ -31,8 +40,10 @@ module MultipartUploadUtils
|
|
31
40
|
# @param [String] mimetype Mimetype of incoming file
|
32
41
|
# @param [FilestackSecurity] security Security object with
|
33
42
|
# policy/signature
|
43
|
+
# @param [String] storage Default storage to be used for uploads
|
34
44
|
# @param [Hash] options User-defined options for
|
35
45
|
# multipart uploads
|
46
|
+
# @param [Bool] intelligent Upload file using Filestack Intelligent Ingestion
|
36
47
|
#
|
37
48
|
# @return [Typhoeus::Response]
|
38
49
|
def multipart_start(apikey, filename, filesize, mimetype, security, storage, options = {}, intelligent)
|
@@ -67,22 +78,21 @@ module MultipartUploadUtils
|
|
67
78
|
#
|
68
79
|
# @param [String] apikey Filestack API key
|
69
80
|
# @param [String] filename Name of incoming file
|
70
|
-
# @param [String] filepath Local path to file
|
71
81
|
# @param [Int] filesize Size of incoming file
|
72
82
|
# @param [Typhoeus::Response] start_response Response body from
|
73
83
|
# multipart_start
|
84
|
+
# @param [String] storage Default storage to be used for uploads
|
74
85
|
# @param [Hash] options User-defined options for
|
75
86
|
# multipart uploads
|
76
87
|
#
|
77
88
|
# @return [Array]
|
78
|
-
def create_upload_jobs(apikey, filename,
|
89
|
+
def create_upload_jobs(apikey, filename, filesize, start_response, storage, options)
|
79
90
|
jobs = []
|
80
91
|
part = 1
|
81
92
|
seek_point = 0
|
82
93
|
while seek_point < filesize
|
83
94
|
part_info = {
|
84
95
|
seek_point: seek_point,
|
85
|
-
filepath: filepath,
|
86
96
|
filename: filename,
|
87
97
|
apikey: apikey,
|
88
98
|
part: part,
|
@@ -92,7 +102,7 @@ module MultipartUploadUtils
|
|
92
102
|
upload_id: start_response['upload_id'],
|
93
103
|
location_url: start_response['location_url'],
|
94
104
|
start_response: start_response,
|
95
|
-
store: { location: storage }
|
105
|
+
store: { location: storage },
|
96
106
|
}
|
97
107
|
|
98
108
|
part_info[:store].merge!(options) if options
|
@@ -116,15 +126,16 @@ module MultipartUploadUtils
|
|
116
126
|
# @param [Hash] job Hash of options needed
|
117
127
|
# to upload a chunk
|
118
128
|
# @param [String] apikey Filestack API key
|
119
|
-
# @param [String]
|
129
|
+
# @param [String] filepath Location url given back
|
120
130
|
# from endpoint
|
121
|
-
# @param [
|
131
|
+
# @param [StringIO] io The IO object
|
122
132
|
# @param [Hash] options User-defined options for
|
123
133
|
# multipart uploads
|
134
|
+
# @param [String] storage Default storage to be used for uploads
|
124
135
|
#
|
125
136
|
# @return [Typhoeus::Response]
|
126
|
-
def upload_chunk(job, apikey, filepath, options, storage)
|
127
|
-
file = File.open(filepath)
|
137
|
+
def upload_chunk(job, apikey, filepath, io, options, storage)
|
138
|
+
file = filepath ? File.open(filepath) : io
|
128
139
|
file.seek(job[:seek_point])
|
129
140
|
chunk = file.read(FilestackConfig::DEFAULT_CHUNK_SIZE)
|
130
141
|
|
@@ -139,7 +150,6 @@ module MultipartUploadUtils
|
|
139
150
|
region: job[:region],
|
140
151
|
upload_id: job[:upload_id],
|
141
152
|
store: { location: storage },
|
142
|
-
file: Tempfile.new(job[:filename])
|
143
153
|
}
|
144
154
|
data = data.merge!(options) if options
|
145
155
|
|
@@ -158,13 +168,14 @@ module MultipartUploadUtils
|
|
158
168
|
# @param [String] filepath Local path to file
|
159
169
|
# @param [Hash] options User-defined options for
|
160
170
|
# multipart uploads
|
171
|
+
# @param [String] storage Default storage to be used for uploads
|
161
172
|
#
|
162
173
|
# @return [Array] Array of parts/etags strings
|
163
|
-
def run_uploads(jobs, apikey, filepath, options, storage)
|
174
|
+
def run_uploads(jobs, apikey, filepath, io, options, storage)
|
164
175
|
bar = ProgressBar.new(jobs.length)
|
165
176
|
results = Parallel.map(jobs, in_threads: 4) do |job|
|
166
177
|
response = upload_chunk(
|
167
|
-
job, apikey, filepath, options, storage
|
178
|
+
job, apikey, filepath, io, options, storage
|
168
179
|
)
|
169
180
|
if response.code == 200
|
170
181
|
bar.increment!
|
@@ -190,6 +201,8 @@ module MultipartUploadUtils
|
|
190
201
|
# part numbers
|
191
202
|
# @param [Hash] options User-defined options for
|
192
203
|
# multipart uploads
|
204
|
+
# @param [String] storage Default storage to be used for uploads
|
205
|
+
# @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
|
193
206
|
#
|
194
207
|
# @return [Typhoeus::Response]
|
195
208
|
def multipart_complete(apikey, filename, filesize, mimetype, start_response, parts_and_etags, options, storage, intelligent = false)
|
@@ -215,32 +228,39 @@ module MultipartUploadUtils
|
|
215
228
|
#
|
216
229
|
# @param [String] apikey Filestack API key
|
217
230
|
# @param [String] filename Name of incoming file
|
231
|
+
# @param [StringIO] io The IO object
|
218
232
|
# @param [FilestackSecurity] security Security object with
|
219
233
|
# policy/signature
|
220
234
|
# @param [Hash] options User-defined options for
|
221
235
|
# multipart uploads
|
236
|
+
# @param [String] storage Default storage to be used for uploads
|
237
|
+
# @param [Boolean] intelligent Upload file using Filestack Intelligent Ingestion
|
222
238
|
#
|
223
239
|
# @return [Hash]
|
224
|
-
def multipart_upload(apikey, filepath, security, options, timeout, storage, intelligent
|
225
|
-
filename, filesize, mimetype =
|
240
|
+
def multipart_upload(apikey, filepath, io, security, options, timeout, storage, intelligent = false)
|
241
|
+
filename, filesize, mimetype = if filepath
|
242
|
+
get_file_attributes(filepath, options)
|
243
|
+
else
|
244
|
+
get_io_attributes(io, options)
|
245
|
+
end
|
226
246
|
|
227
247
|
start_response = multipart_start(
|
228
248
|
apikey, filename, filesize, mimetype, security, storage, options, intelligent
|
229
249
|
)
|
230
250
|
|
231
251
|
jobs = create_upload_jobs(
|
232
|
-
apikey, filename,
|
252
|
+
apikey, filename, filesize, start_response, storage, options
|
233
253
|
)
|
234
254
|
|
235
255
|
if intelligent
|
236
256
|
state = IntelligentState.new
|
237
|
-
run_intelligent_upload_flow(jobs, state, storage)
|
257
|
+
run_intelligent_upload_flow(jobs, filepath, io, state, storage)
|
238
258
|
response_complete = multipart_complete(
|
239
259
|
apikey, filename, filesize, mimetype,
|
240
260
|
start_response, nil, options, storage, intelligent
|
241
261
|
)
|
242
262
|
else
|
243
|
-
parts_and_etags = run_uploads(jobs, apikey, filepath, options, storage)
|
263
|
+
parts_and_etags = run_uploads(jobs, apikey, filepath, io, options, storage)
|
244
264
|
response_complete = multipart_complete(
|
245
265
|
apikey, filename, filesize, mimetype,
|
246
266
|
start_response, parts_and_etags, options, storage
|
@@ -88,7 +88,7 @@ module UploadUtils
|
|
88
88
|
# @param [Hash] options User-defined options for
|
89
89
|
# multipart uploads
|
90
90
|
# @return [Hash]
|
91
|
-
def send_upload(apikey, external_url
|
91
|
+
def send_upload(apikey, external_url = nil, security = nil, options = nil)
|
92
92
|
base = "#{FilestackConfig::CDN_URL}/#{apikey}/#{build_store_task(options)}"
|
93
93
|
|
94
94
|
if security
|
@@ -199,7 +199,7 @@ module IntelligentUtils
|
|
199
199
|
# @param [IntelligentState] state An IntelligentState object
|
200
200
|
#
|
201
201
|
# @return [Array]
|
202
|
-
def run_intelligent_upload_flow(jobs, state, storage)
|
202
|
+
def run_intelligent_upload_flow(jobs, filepath, io, state, storage)
|
203
203
|
bar = ProgressBar.new(jobs.length)
|
204
204
|
generator = create_intelligent_generator(jobs)
|
205
205
|
working_offset = FilestackConfig::DEFAULT_OFFSET_SIZE
|
@@ -207,7 +207,7 @@ module IntelligentUtils
|
|
207
207
|
batch = get_generator_batch(generator)
|
208
208
|
# run parts
|
209
209
|
Parallel.map(batch, in_threads: 4) do |part|
|
210
|
-
state = run_intelligent_uploads(part, state, storage)
|
210
|
+
state = run_intelligent_uploads(part, filepath, io, state, storage)
|
211
211
|
# condition: a chunk has failed but we have not reached the maximum retries
|
212
212
|
while bad_state(state)
|
213
213
|
# condition: timeout to S3, requiring offset size to be changed
|
@@ -219,7 +219,7 @@ module IntelligentUtils
|
|
219
219
|
sleep(state.backoff)
|
220
220
|
end
|
221
221
|
state.add_retry
|
222
|
-
state = run_intelligent_uploads(part, state, storage)
|
222
|
+
state = run_intelligent_uploads(part, filepath, io, state, storage)
|
223
223
|
end
|
224
224
|
raise "Upload has failed. Please try again later." unless state.ok
|
225
225
|
bar.increment!
|
@@ -275,7 +275,7 @@ module IntelligentUtils
|
|
275
275
|
# multipart_start
|
276
276
|
#
|
277
277
|
# @return [Dict]
|
278
|
-
def chunk_job(job, state, apikey, filename,
|
278
|
+
def chunk_job(job, state, apikey, filename, filesize, start_response, storage)
|
279
279
|
offset = 0
|
280
280
|
seek_point = job[:seek_point]
|
281
281
|
chunk_list = []
|
@@ -283,7 +283,6 @@ module IntelligentUtils
|
|
283
283
|
while (offset < FilestackConfig::DEFAULT_CHUNK_SIZE) && (seek_point + offset) < filesize
|
284
284
|
chunk_list.push(
|
285
285
|
seek_point: seek_point,
|
286
|
-
filepath: filepath,
|
287
286
|
filename: filename,
|
288
287
|
apikey: apikey,
|
289
288
|
part: job[:part],
|
@@ -307,15 +306,14 @@ module IntelligentUtils
|
|
307
306
|
# @param [IntelligentState] state An IntelligentState object
|
308
307
|
#
|
309
308
|
# @return [IntelligentState]
|
310
|
-
def run_intelligent_uploads(part, state, storage)
|
309
|
+
def run_intelligent_uploads(part, filepath, io, state, storage)
|
311
310
|
failed = false
|
312
311
|
chunks = chunk_job(
|
313
|
-
part, state, part[:apikey], part[:filename], part[:
|
314
|
-
part[:filesize], part[:start_response], storage
|
312
|
+
part, state, part[:apikey], part[:filename], part[:filesize], part[:start_response], storage
|
315
313
|
)
|
316
314
|
Parallel.map(chunks, in_threads: 3) do |chunk|
|
317
315
|
begin
|
318
|
-
upload_chunk_intelligently(chunk, state, part[:apikey],
|
316
|
+
upload_chunk_intelligently(chunk, state, part[:apikey], filepath, io, part[:options], storage)
|
319
317
|
rescue => e
|
320
318
|
state.error_type = e.message
|
321
319
|
failed = true
|
@@ -364,8 +362,8 @@ module IntelligentUtils
|
|
364
362
|
# multipart uploads
|
365
363
|
#
|
366
364
|
# @return [Typhoeus::Response]
|
367
|
-
def upload_chunk_intelligently(job, state, apikey, filepath, options, storage)
|
368
|
-
file = File.open(filepath)
|
365
|
+
def upload_chunk_intelligently(job, state, apikey, filepath, io, options, storage)
|
366
|
+
file = filepath ? File.open(filepath) : io
|
369
367
|
file.seek(job[:seek_point] + job[:offset])
|
370
368
|
|
371
369
|
chunk = file.read(state.offset)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: filestack
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Filestack
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: typhoeus
|