google-cloud-storage 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +2 -2
- data/lib/google-cloud-storage.rb +27 -2
- data/lib/google/cloud/storage.rb +101 -11
- data/lib/google/cloud/storage/bucket.rb +53 -21
- data/lib/google/cloud/storage/bucket/acl.rb +24 -24
- data/lib/google/cloud/storage/bucket/cors.rb +3 -3
- data/lib/google/cloud/storage/bucket/list.rb +3 -3
- data/lib/google/cloud/storage/credentials.rb +9 -8
- data/lib/google/cloud/storage/errors.rb +2 -2
- data/lib/google/cloud/storage/file.rb +74 -15
- data/lib/google/cloud/storage/file/acl.rb +13 -13
- data/lib/google/cloud/storage/file/list.rb +3 -3
- data/lib/google/cloud/storage/file/signer.rb +12 -8
- data/lib/google/cloud/storage/file/verifier.rb +12 -10
- data/lib/google/cloud/storage/notification.rb +3 -3
- data/lib/google/cloud/storage/policy.rb +3 -3
- data/lib/google/cloud/storage/post_object.rb +2 -2
- data/lib/google/cloud/storage/project.rb +5 -15
- data/lib/google/cloud/storage/service.rb +187 -22
- data/lib/google/cloud/storage/version.rb +3 -3
- metadata +11 -11
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2015 Google
|
1
|
+
# Copyright 2015 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -125,7 +125,7 @@ module Google
|
|
125
125
|
@origin = Array(origin)
|
126
126
|
@methods = Array(methods)
|
127
127
|
@headers = Array(headers)
|
128
|
-
@max_age = (max_age||1800)
|
128
|
+
@max_age = (max_age || 1800)
|
129
129
|
end
|
130
130
|
|
131
131
|
def to_gapi
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2015 Google
|
1
|
+
# Copyright 2015 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -163,7 +163,7 @@ module Google
|
|
163
163
|
##
|
164
164
|
# Raise an error unless an active connection is available.
|
165
165
|
def ensure_service!
|
166
|
-
|
166
|
+
raise "Must have active connection" unless @service
|
167
167
|
end
|
168
168
|
end
|
169
169
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2014 Google
|
1
|
+
# Copyright 2014 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -38,19 +38,20 @@ module Google
|
|
38
38
|
# storage.project_id #=> "my-project"
|
39
39
|
#
|
40
40
|
class Credentials < Google::Auth::Credentials
|
41
|
-
SCOPE =
|
42
|
-
|
41
|
+
SCOPE = \
|
42
|
+
["https://www.googleapis.com/auth/devstorage.full_control"].freeze
|
43
|
+
PATH_ENV_VARS = %w[STORAGE_CREDENTIALS
|
43
44
|
STORAGE_KEYFILE
|
44
45
|
GOOGLE_CLOUD_CREDENTIALS
|
45
46
|
GOOGLE_CLOUD_KEYFILE
|
46
|
-
GCLOUD_KEYFILE
|
47
|
-
JSON_ENV_VARS = %w
|
47
|
+
GCLOUD_KEYFILE].freeze
|
48
|
+
JSON_ENV_VARS = %w[STORAGE_CREDENTIALS_JSON
|
48
49
|
STORAGE_KEYFILE_JSON
|
49
50
|
GOOGLE_CLOUD_CREDENTIALS_JSON
|
50
51
|
GOOGLE_CLOUD_KEYFILE_JSON
|
51
|
-
GCLOUD_KEYFILE_JSON
|
52
|
+
GCLOUD_KEYFILE_JSON].freeze
|
52
53
|
DEFAULT_PATHS = \
|
53
|
-
["~/.config/gcloud/application_default_credentials.json"]
|
54
|
+
["~/.config/gcloud/application_default_credentials.json"].freeze
|
54
55
|
end
|
55
56
|
end
|
56
57
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2014 Google
|
1
|
+
# Copyright 2014 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2014 Google
|
1
|
+
# Copyright 2014 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -13,11 +13,11 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
-
require "uri"
|
17
16
|
require "google/cloud/storage/file/acl"
|
18
17
|
require "google/cloud/storage/file/list"
|
19
18
|
require "google/cloud/storage/file/verifier"
|
20
19
|
require "google/cloud/storage/file/signer"
|
20
|
+
require "zlib"
|
21
21
|
|
22
22
|
module Google
|
23
23
|
module Cloud
|
@@ -438,6 +438,13 @@ module Google
|
|
438
438
|
# @param [String] encryption_key Optional. The customer-supplied,
|
439
439
|
# AES-256 encryption key used to encrypt the file, if one was provided
|
440
440
|
# to {Bucket#create_file}.
|
441
|
+
# @param [Boolean] skip_decompress Optional. If `true`, the data for a
|
442
|
+
# Storage object returning a `Content-Encoding: gzip` response header
|
443
|
+
# will *not* be automatically decompressed by this client library. The
|
444
|
+
# default is `nil`. Note that all requests by this client library send
|
445
|
+
# the `Accept-Encoding: gzip` header, so decompressive transcoding is
|
446
|
+
# not performed in the Storage service. (See [Transcoding of
|
447
|
+
# gzip-compressed files](https://cloud.google.com/storage/docs/transcoding))
|
441
448
|
#
|
442
449
|
# @return [IO] Returns an IO object representing the file data. This
|
443
450
|
# will ordinarily be a `::File` object referencing the local file
|
@@ -497,7 +504,7 @@ module Google
|
|
497
504
|
# downloaded.rewind
|
498
505
|
# downloaded.read #=> "Hello world!"
|
499
506
|
#
|
500
|
-
# @example Download a public file with an unauthenticated client
|
507
|
+
# @example Download a public file with an unauthenticated client.
|
501
508
|
# require "google/cloud/storage"
|
502
509
|
#
|
503
510
|
# storage = Google::Cloud::Storage.anonymous
|
@@ -509,17 +516,49 @@ module Google
|
|
509
516
|
# downloaded.rewind
|
510
517
|
# downloaded.read #=> "Hello world!"
|
511
518
|
#
|
512
|
-
|
519
|
+
# @example Upload and download gzip-encoded file data.
|
520
|
+
# require "zlib"
|
521
|
+
# require "google/cloud/storage"
|
522
|
+
#
|
523
|
+
# storage = Google::Cloud::Storage.new
|
524
|
+
#
|
525
|
+
# gz = StringIO.new ""
|
526
|
+
# z = Zlib::GzipWriter.new gz
|
527
|
+
# z.write "Hello world!"
|
528
|
+
# z.close
|
529
|
+
# data = StringIO.new gz.string
|
530
|
+
#
|
531
|
+
# bucket = storage.bucket "my-bucket"
|
532
|
+
#
|
533
|
+
# bucket.create_file data, "path/to/gzipped.txt",
|
534
|
+
# content_encoding: "gzip"
|
535
|
+
#
|
536
|
+
# file = bucket.file "path/to/gzipped.txt"
|
537
|
+
#
|
538
|
+
# # The downloaded data is decompressed by default.
|
539
|
+
# file.download "path/to/downloaded/hello.txt"
|
540
|
+
#
|
541
|
+
# # The downloaded data remains compressed with skip_decompress.
|
542
|
+
# file.download "path/to/downloaded/gzipped.txt",
|
543
|
+
# skip_decompress: true
|
544
|
+
#
|
545
|
+
def download path = nil, verify: :md5, encryption_key: nil,
|
546
|
+
skip_decompress: nil
|
513
547
|
ensure_service!
|
514
548
|
if path.nil?
|
515
549
|
path = StringIO.new
|
516
550
|
path.set_encoding "ASCII-8BIT"
|
517
551
|
end
|
518
|
-
file = service.download_file \
|
552
|
+
file, resp = service.download_file \
|
519
553
|
bucket, name, path, key: encryption_key, user_project: user_project
|
520
554
|
# FIX: downloading with encryption key will return nil
|
521
555
|
file ||= ::File.new(path)
|
522
556
|
verify_file! file, verify
|
557
|
+
if !skip_decompress &&
|
558
|
+
Array(resp.header["Content-Encoding"]).include?("gzip")
|
559
|
+
file = gzip_decompress file
|
560
|
+
end
|
561
|
+
file
|
523
562
|
end
|
524
563
|
|
525
564
|
##
|
@@ -774,7 +813,7 @@ module Google
|
|
774
813
|
def public_url protocol: :https
|
775
814
|
"#{protocol}://storage.googleapis.com/#{bucket}/#{name}"
|
776
815
|
end
|
777
|
-
|
816
|
+
alias url public_url
|
778
817
|
|
779
818
|
##
|
780
819
|
# Access without authentication can be granted to a File for a specified
|
@@ -977,7 +1016,7 @@ module Google
|
|
977
1016
|
@lazy = nil
|
978
1017
|
self
|
979
1018
|
end
|
980
|
-
|
1019
|
+
alias refresh! reload!
|
981
1020
|
|
982
1021
|
##
|
983
1022
|
# Determines whether the file exists in the Storage service.
|
@@ -1037,7 +1076,7 @@ module Google
|
|
1037
1076
|
##
|
1038
1077
|
# Raise an error unless an active service is available.
|
1039
1078
|
def ensure_service!
|
1040
|
-
|
1079
|
+
raise "Must have active connection" unless service
|
1041
1080
|
end
|
1042
1081
|
|
1043
1082
|
##
|
@@ -1056,12 +1095,12 @@ module Google
|
|
1056
1095
|
|
1057
1096
|
ensure_service!
|
1058
1097
|
|
1059
|
-
if attributes.include? :storage_class
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1098
|
+
@gapi = if attributes.include? :storage_class
|
1099
|
+
rewrite_gapi bucket, name, update_gapi
|
1100
|
+
else
|
1101
|
+
service.patch_file \
|
1102
|
+
bucket, name, update_gapi, user_project: user_project
|
1103
|
+
end
|
1065
1104
|
end
|
1066
1105
|
|
1067
1106
|
def gapi_from_attrs *attributes
|
@@ -1107,6 +1146,26 @@ module Google
|
|
1107
1146
|
file
|
1108
1147
|
end
|
1109
1148
|
|
1149
|
+
# @return [IO] Returns an IO object representing the file data. This
|
1150
|
+
# will either be a `::File` object referencing the local file
|
1151
|
+
# system or a StringIO instance.
|
1152
|
+
def gzip_decompress local_file
|
1153
|
+
if local_file.respond_to? :path
|
1154
|
+
gz = ::File.open(Pathname(local_file).to_path, "rb") do |f|
|
1155
|
+
Zlib::GzipReader.new(StringIO.new(f.read))
|
1156
|
+
end
|
1157
|
+
uncompressed_string = gz.read
|
1158
|
+
::File.open(Pathname(local_file).to_path, "w") do |f|
|
1159
|
+
f.write uncompressed_string
|
1160
|
+
f
|
1161
|
+
end
|
1162
|
+
else # local_file is StringIO
|
1163
|
+
local_file.rewind
|
1164
|
+
gz = Zlib::GzipReader.new StringIO.new(local_file.read)
|
1165
|
+
StringIO.new gz.read
|
1166
|
+
end
|
1167
|
+
end
|
1168
|
+
|
1110
1169
|
def storage_class_for str
|
1111
1170
|
return nil if str.nil?
|
1112
1171
|
{ "durable_reduced_availability" => "DURABLE_REDUCED_AVAILABILITY",
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2015 Google
|
1
|
+
# Copyright 2015 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -48,7 +48,7 @@ module Google
|
|
48
48
|
"project_private" => "projectPrivate",
|
49
49
|
"publicRead" => "publicRead",
|
50
50
|
"public" => "publicRead",
|
51
|
-
"public_read" => "publicRead" }
|
51
|
+
"public_read" => "publicRead" }.freeze
|
52
52
|
|
53
53
|
##
|
54
54
|
# A boolean value or a project ID string to indicate the project to
|
@@ -101,7 +101,7 @@ module Google
|
|
101
101
|
@owners = entities_from_acls acls, "OWNER"
|
102
102
|
@readers = entities_from_acls acls, "READER"
|
103
103
|
end
|
104
|
-
|
104
|
+
alias refresh! reload!
|
105
105
|
|
106
106
|
##
|
107
107
|
# Lists the owners of the file.
|
@@ -303,10 +303,10 @@ module Google
|
|
303
303
|
def auth!
|
304
304
|
update_predefined_acl! "authenticatedRead"
|
305
305
|
end
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
306
|
+
alias authenticatedRead! auth!
|
307
|
+
alias auth_read! auth!
|
308
|
+
alias authenticated! auth!
|
309
|
+
alias authenticated_read! auth!
|
310
310
|
|
311
311
|
##
|
312
312
|
# Convenience method to apply the `bucketOwnerFullControl` predefined
|
@@ -325,7 +325,7 @@ module Google
|
|
325
325
|
def owner_full!
|
326
326
|
update_predefined_acl! "bucketOwnerFullControl"
|
327
327
|
end
|
328
|
-
|
328
|
+
alias bucketOwnerFullControl! owner_full!
|
329
329
|
|
330
330
|
##
|
331
331
|
# Convenience method to apply the `bucketOwnerRead` predefined ACL
|
@@ -344,7 +344,7 @@ module Google
|
|
344
344
|
def owner_read!
|
345
345
|
update_predefined_acl! "bucketOwnerRead"
|
346
346
|
end
|
347
|
-
|
347
|
+
alias bucketOwnerRead! owner_read!
|
348
348
|
|
349
349
|
##
|
350
350
|
# Convenience method to apply the `private` predefined ACL
|
@@ -381,7 +381,7 @@ module Google
|
|
381
381
|
def project_private!
|
382
382
|
update_predefined_acl! "projectPrivate"
|
383
383
|
end
|
384
|
-
|
384
|
+
alias projectPrivate! project_private!
|
385
385
|
|
386
386
|
##
|
387
387
|
# Convenience method to apply the `publicRead` predefined ACL
|
@@ -400,8 +400,8 @@ module Google
|
|
400
400
|
def public!
|
401
401
|
update_predefined_acl! "publicRead"
|
402
402
|
end
|
403
|
-
|
404
|
-
|
403
|
+
alias publicRead! public!
|
404
|
+
alias public_read! public!
|
405
405
|
|
406
406
|
protected
|
407
407
|
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2015 Google
|
1
|
+
# Copyright 2015 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -182,7 +182,7 @@ module Google
|
|
182
182
|
##
|
183
183
|
# Raise an error unless an active service is available.
|
184
184
|
def ensure_service!
|
185
|
-
|
185
|
+
raise "Must have active connection" unless @service
|
186
186
|
end
|
187
187
|
end
|
188
188
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2017 Google
|
1
|
+
# Copyright 2017 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
|
16
16
|
require "base64"
|
17
|
+
require "cgi"
|
17
18
|
require "openssl"
|
18
19
|
require "google/cloud/storage/errors"
|
19
20
|
|
@@ -41,7 +42,10 @@ module Google
|
|
41
42
|
##
|
42
43
|
# The external path to the file.
|
43
44
|
def ext_path
|
44
|
-
|
45
|
+
escaped_path = String(@path).split("/").map do |node|
|
46
|
+
CGI.escape node
|
47
|
+
end.join("/")
|
48
|
+
"/#{CGI.escape @bucket}/#{escaped_path}"
|
45
49
|
end
|
46
50
|
|
47
51
|
##
|
@@ -53,7 +57,7 @@ module Google
|
|
53
57
|
def apply_option_defaults options
|
54
58
|
adjusted_expires = (Time.now.utc + (options[:expires] || 300)).to_i
|
55
59
|
options[:expires] = adjusted_expires
|
56
|
-
options[:method]
|
60
|
+
options[:method] ||= "GET"
|
57
61
|
options
|
58
62
|
end
|
59
63
|
|
@@ -81,12 +85,12 @@ module Google
|
|
81
85
|
}
|
82
86
|
|
83
87
|
p = options[:policy] || {}
|
84
|
-
|
88
|
+
raise "Policy must be given in a Hash" unless p.is_a? Hash
|
85
89
|
|
86
90
|
i = determine_issuer options
|
87
91
|
s = determine_signing_key options
|
88
92
|
|
89
|
-
|
93
|
+
raise SignedUrlUnavailable unless i && s
|
90
94
|
|
91
95
|
policy_str = p.to_json
|
92
96
|
policy = Base64.strict_encode64(policy_str).delete("\n")
|
@@ -106,7 +110,7 @@ module Google
|
|
106
110
|
i = determine_issuer options
|
107
111
|
s = determine_signing_key options
|
108
112
|
|
109
|
-
|
113
|
+
raise SignedUrlUnavailable unless i && s
|
110
114
|
|
111
115
|
sig = generate_signature s, signature_str(options)
|
112
116
|
generate_signed_url i, sig, options[:expires], options[:query]
|
@@ -136,7 +140,7 @@ module Google
|
|
136
140
|
|
137
141
|
def format_extension_headers headers
|
138
142
|
return "" if headers.nil?
|
139
|
-
|
143
|
+
raise "Headers must be given in a Hash" unless headers.is_a? Hash
|
140
144
|
flatten = headers.map do |key, value|
|
141
145
|
"#{key.to_s.downcase}:#{value.gsub(/\s+/, ' ')}\n"
|
142
146
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
# Copyright 2014 Google
|
1
|
+
# Copyright 2014 Google LLC
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
5
5
|
# You may obtain a copy of the License at
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
8
|
#
|
9
9
|
# Unless required by applicable law or agreed to in writing, software
|
10
10
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
@@ -30,17 +30,15 @@ module Google
|
|
30
30
|
def self.verify_md5! gcloud_file, local_file
|
31
31
|
gcloud_digest = gcloud_file.md5
|
32
32
|
local_digest = md5_for local_file
|
33
|
-
if gcloud_digest
|
34
|
-
|
35
|
-
end
|
33
|
+
return if gcloud_digest == local_digest
|
34
|
+
raise FileVerificationError.for_md5(gcloud_digest, local_digest)
|
36
35
|
end
|
37
36
|
|
38
37
|
def self.verify_crc32c! gcloud_file, local_file
|
39
38
|
gcloud_digest = gcloud_file.crc32c
|
40
39
|
local_digest = crc32c_for local_file
|
41
|
-
if gcloud_digest
|
42
|
-
|
43
|
-
end
|
40
|
+
return if gcloud_digest == local_digest
|
41
|
+
raise FileVerificationError.for_crc32c(gcloud_digest, local_digest)
|
44
42
|
end
|
45
43
|
|
46
44
|
def self.verify_md5 gcloud_file, local_file
|
@@ -58,7 +56,9 @@ module Google
|
|
58
56
|
end
|
59
57
|
else # StringIO
|
60
58
|
local_file.rewind
|
61
|
-
::Digest::MD5.base64digest local_file.read
|
59
|
+
md5 = ::Digest::MD5.base64digest local_file.read
|
60
|
+
local_file.rewind
|
61
|
+
md5
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
@@ -69,7 +69,9 @@ module Google
|
|
69
69
|
end
|
70
70
|
else # StringIO
|
71
71
|
local_file.rewind
|
72
|
-
::Digest::CRC32c.base64digest local_file.read
|
72
|
+
crc32c = ::Digest::CRC32c.base64digest local_file.read
|
73
|
+
local_file.rewind
|
74
|
+
crc32c
|
73
75
|
end
|
74
76
|
end
|
75
77
|
end
|