| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Net::Google::Drive; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
2638
|
use 5.008001; |
|
|
1
|
|
|
|
|
4
|
|
|
4
|
1
|
|
|
1
|
|
8
|
use strict; |
|
|
1
|
|
|
|
|
10
|
|
|
|
1
|
|
|
|
|
36
|
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
26
|
|
|
6
|
1
|
|
|
1
|
|
4
|
use utf8; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
4
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
725
|
use LWP::UserAgent; |
|
|
1
|
|
|
|
|
48215
|
|
|
|
1
|
|
|
|
|
39
|
|
|
9
|
1
|
|
|
1
|
|
10
|
use HTTP::Request; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
26
|
|
|
10
|
1
|
|
|
1
|
|
791
|
use JSON::XS; |
|
|
1
|
|
|
|
|
5231
|
|
|
|
1
|
|
|
|
|
58
|
|
|
11
|
1
|
|
|
1
|
|
8
|
use URI; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
26
|
|
|
12
|
1
|
|
|
1
|
|
5
|
use File::Basename; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
67
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
1
|
|
|
1
|
|
10
|
use Carp qw/carp croak/; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
48
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
1
|
|
|
1
|
|
502
|
use Net::Google::OAuth; |
|
|
1
|
|
|
|
|
1502
|
|
|
|
1
|
|
|
|
|
2714
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
our $DOWNLOAD_BUFF_SIZE = 1024; |
|
21
|
|
|
|
|
|
|
our $UPLOAD_BUFF_SIZE = 256 * 1024; |
|
22
|
|
|
|
|
|
|
our $FILE_API_URL = 'https://www.googleapis.com/drive/v3/files'; |
|
23
|
|
|
|
|
|
|
our $FILE_API2_URL = 'https://www.googleapis.com/drive/v2/files'; |
|
24
|
|
|
|
|
|
|
our $UPLOAD_FILE_API_URL = 'https://www.googleapis.com/upload/drive/v3/files'; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub new { |
|
27
|
1
|
|
|
1
|
1
|
163
|
my ($class, %opt) = @_; |
|
28
|
|
|
|
|
|
|
|
|
29
|
1
|
|
|
|
|
4
|
my $self = {}; |
|
30
|
1
|
|
33
|
|
|
6
|
my $client_id = $opt{-client_id} // croak "You must specify '-client_id' param"; |
|
31
|
1
|
|
33
|
|
|
4
|
my $client_secret = $opt{-client_secret} // croak "You must specify '-client_secret' param"; |
|
32
|
1
|
|
33
|
|
|
4
|
$self->{access_token} = $opt{-access_token} // croak "You must specify '-access_token' param"; |
|
33
|
1
|
|
33
|
|
|
15
|
$self->{refresh_token} = $opt{-refresh_token} // croak "You must specify '-refresh_token' param"; |
|
34
|
1
|
|
|
|
|
13
|
$self->{ua} = LWP::UserAgent->new(); |
|
35
|
|
|
|
|
|
|
|
|
36
|
1
|
|
|
|
|
2929
|
$self->{oauth} = Net::Google::OAuth->new( |
|
37
|
|
|
|
|
|
|
-client_id => $client_id, |
|
38
|
|
|
|
|
|
|
-client_secret => $client_secret, |
|
39
|
|
|
|
|
|
|
); |
|
40
|
1
|
|
|
|
|
240386
|
bless $self, $class; |
|
41
|
1
|
|
|
|
|
7
|
return $self; |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub searchFileByName { |
|
45
|
2
|
|
|
2
|
1
|
132709
|
my ($self, %opt) = @_; |
|
46
|
2
|
|
33
|
|
|
11
|
my $filename = $opt{-filename} || croak "You must specify '-filename' param"; |
|
47
|
|
|
|
|
|
|
|
|
48
|
2
|
|
|
|
|
14
|
my $search_res = $self->__searchFile('name=\'' . $filename . "'"); |
|
49
|
|
|
|
|
|
|
|
|
50
|
2
|
|
|
|
|
12
|
return $search_res; |
|
51
|
|
|
|
|
|
|
} |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub searchFileByNameContains { |
|
54
|
1
|
|
|
1
|
1
|
526
|
my ($self, %opt) = @_; |
|
55
|
1
|
|
33
|
|
|
8
|
my $filename = $opt{-filename} || croak "You must specify '-filename' param"; |
|
56
|
|
|
|
|
|
|
|
|
57
|
1
|
|
|
|
|
6
|
my $search_res = $self->__searchFile('name contains \'' . $filename . "'"); |
|
58
|
|
|
|
|
|
|
|
|
59
|
1
|
|
|
|
|
8
|
return $search_res; |
|
60
|
|
|
|
|
|
|
} |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub downloadFile { |
|
64
|
1
|
|
|
1
|
1
|
1211
|
my ($self, %opt) = @_; |
|
65
|
1
|
|
33
|
|
|
5
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
|
66
|
1
|
|
33
|
|
|
6
|
my $dest_file = $opt{-dest_file} || croak "You must specify '-dest_file' param"; |
|
67
|
1
|
|
|
|
|
3
|
my $ua = $self->{ua}; |
|
68
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
|
69
|
|
|
|
|
|
|
|
|
70
|
1
|
|
|
|
|
13
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id)); |
|
71
|
1
|
|
|
|
|
119
|
$uri->query_form( |
|
72
|
|
|
|
|
|
|
'alt' => 'media', |
|
73
|
|
|
|
|
|
|
); |
|
74
|
1
|
|
|
|
|
95
|
my $headers = [ |
|
75
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
76
|
|
|
|
|
|
|
]; |
|
77
|
|
|
|
|
|
|
|
|
78
|
1
|
|
|
|
|
8
|
my $request = HTTP::Request->new( 'GET', |
|
79
|
|
|
|
|
|
|
$uri, |
|
80
|
|
|
|
|
|
|
$headers, |
|
81
|
|
|
|
|
|
|
); |
|
82
|
|
|
|
|
|
|
|
|
83
|
1
|
|
|
|
|
152
|
my $FL; |
|
84
|
|
|
|
|
|
|
my $response = $ua->request($request, sub { |
|
85
|
3
|
100
|
|
3
|
|
727858
|
if (not $FL) { |
|
86
|
1
|
50
|
|
|
|
170
|
open $FL, ">$dest_file" or croak "Cant open $dest_file to write $!"; |
|
87
|
1
|
|
|
|
|
8
|
binmode $FL; |
|
88
|
|
|
|
|
|
|
} |
|
89
|
3
|
|
|
|
|
13
|
print $FL $_[0]; |
|
90
|
|
|
|
|
|
|
}, |
|
91
|
1
|
|
|
|
|
9
|
$DOWNLOAD_BUFF_SIZE |
|
92
|
|
|
|
|
|
|
); |
|
93
|
1
|
50
|
|
|
|
2057
|
if ($FL) { |
|
94
|
1
|
|
|
|
|
62
|
close $FL; |
|
95
|
|
|
|
|
|
|
} |
|
96
|
1
|
|
|
|
|
8
|
my $response_code = $response->code(); |
|
97
|
1
|
50
|
|
|
|
16
|
if ($response_code != 200) { |
|
98
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
|
99
|
0
|
|
|
|
|
0
|
croak "Can't download file id: $file_id to destination file: $dest_file. Code: $response_code. Message: '$error_message'"; |
|
100
|
|
|
|
|
|
|
} |
|
101
|
1
|
|
|
|
|
23
|
return 1; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
sub deleteFile { |
|
105
|
1
|
|
|
1
|
1
|
944
|
my ($self, %opt) = @_; |
|
106
|
1
|
|
33
|
|
|
6
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
|
107
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
|
108
|
|
|
|
|
|
|
|
|
109
|
1
|
|
|
|
|
14
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id)); |
|
110
|
|
|
|
|
|
|
|
|
111
|
1
|
|
|
|
|
405
|
my $headers = [ |
|
112
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
113
|
|
|
|
|
|
|
]; |
|
114
|
|
|
|
|
|
|
|
|
115
|
1
|
|
|
|
|
10
|
my $request = HTTP::Request->new( 'DELETE', |
|
116
|
|
|
|
|
|
|
$uri, |
|
117
|
|
|
|
|
|
|
$headers, |
|
118
|
|
|
|
|
|
|
); |
|
119
|
1
|
|
|
|
|
136
|
my $response = $self->{ua}->request($request); |
|
120
|
1
|
|
|
|
|
639894
|
my $response_code = $response->code(); |
|
121
|
1
|
50
|
|
|
|
16
|
if ($response_code =~ /^[^2]/) { |
|
122
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
|
123
|
0
|
|
|
|
|
0
|
croak "Can't delete file id: $file_id. Code: $response_code. Message: $error_message"; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
1
|
|
|
|
|
22
|
return 1; |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub uploadFile { |
|
129
|
1
|
|
|
1
|
1
|
1194
|
my ($self, %opt) = @_; |
|
130
|
1
|
|
33
|
|
|
21
|
my $source_file = $opt{-source_file} || croak "You must specify '-source_file' param"; |
|
131
|
|
|
|
|
|
|
|
|
132
|
1
|
50
|
|
|
|
27
|
if (not -f $source_file) { |
|
133
|
0
|
|
|
|
|
0
|
croak "File: $source_file not exists"; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
1
|
|
|
|
|
14
|
my $file_size = (stat $source_file)[7]; |
|
137
|
1
|
|
|
|
|
9
|
my $part_upload_uri = $self->__createEmptyFile($source_file, $file_size, $opt{-parents}); |
|
138
|
1
|
50
|
|
|
|
91
|
open my $FH, "<$source_file" or croak "Can't open file: $source_file $!"; |
|
139
|
1
|
|
|
|
|
6
|
binmode $FH; |
|
140
|
|
|
|
|
|
|
|
|
141
|
1
|
|
|
|
|
2
|
my $filebuf; |
|
142
|
1
|
|
|
|
|
502
|
my $uri = URI->new($part_upload_uri); |
|
143
|
1
|
|
|
|
|
247
|
my $start_byte = 0; |
|
144
|
1
|
|
|
|
|
522
|
while (my $bytes = read($FH, $filebuf, $UPLOAD_BUFF_SIZE)) { |
|
145
|
4
|
|
|
|
|
21
|
my $end_byte = $start_byte + $bytes - 1; |
|
146
|
4
|
|
|
|
|
33
|
my $headers = [ |
|
147
|
|
|
|
|
|
|
'Content-Length' => $bytes, |
|
148
|
|
|
|
|
|
|
'Content-Range' => sprintf("bytes %d-%d/%d", $start_byte, $end_byte, $file_size), |
|
149
|
|
|
|
|
|
|
]; |
|
150
|
4
|
|
|
|
|
42
|
my $request = HTTP::Request->new('PUT', $uri, $headers, $filebuf); |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Send request to upload part of file |
|
153
|
4
|
|
|
|
|
1430
|
my $response = $self->{ua}->request($request); |
|
154
|
4
|
|
|
|
|
1478671
|
my $response_code = $response->code(); |
|
155
|
|
|
|
|
|
|
# On end part, response code is 200, on middle part is 308 |
|
156
|
4
|
100
|
66
|
|
|
99
|
if ($response_code == 200 || $response_code == 201) { |
|
|
|
50
|
|
|
|
|
|
|
157
|
1
|
50
|
|
|
|
5
|
if ($end_byte + 1 != $file_size) { |
|
158
|
0
|
|
|
|
|
0
|
croak "Server return code: $response_code on upload file, but file is not fully uploaded. End byte: $end_byte. File size: $file_size. File: $source_file"; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
1
|
|
|
|
|
6
|
return decode_json($response->content()); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
elsif ($response_code != 308) { |
|
163
|
0
|
|
|
|
|
0
|
croak "Wrong response code on upload part file. Code: $response_code. File: $source_file"; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
3
|
|
|
|
|
681
|
$start_byte += $bytes; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
0
|
|
|
|
|
0
|
close $FH; |
|
168
|
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
0
|
return; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
sub setFilePermission { |
|
173
|
3
|
|
|
3
|
1
|
1710
|
my ($self, %opt) = @_; |
|
174
|
3
|
|
33
|
|
|
13
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
|
175
|
3
|
|
33
|
|
|
11
|
my $permission_type = $opt{-type} || croak "You must specify '-type' param"; |
|
176
|
3
|
|
33
|
|
|
8
|
my $role = $opt{-role} || croak "You must specify '-role' param"; |
|
177
|
3
|
|
|
|
|
16
|
my %valid_permissions = ( |
|
178
|
|
|
|
|
|
|
'user' => 1, |
|
179
|
|
|
|
|
|
|
'group' => 1, |
|
180
|
|
|
|
|
|
|
'domain' => 1, |
|
181
|
|
|
|
|
|
|
'anyone' => 1, |
|
182
|
|
|
|
|
|
|
); |
|
183
|
|
|
|
|
|
|
|
|
184
|
3
|
|
|
|
|
15
|
my %valid_roles = ( |
|
185
|
|
|
|
|
|
|
'owner' => 1, |
|
186
|
|
|
|
|
|
|
'organizer' => 1, |
|
187
|
|
|
|
|
|
|
'fileOrganizer' => 1, |
|
188
|
|
|
|
|
|
|
'writer' => 1, |
|
189
|
|
|
|
|
|
|
'commenter' => 1, |
|
190
|
|
|
|
|
|
|
'reader' => 1, |
|
191
|
|
|
|
|
|
|
); |
|
192
|
|
|
|
|
|
|
#Check permission in param |
|
193
|
3
|
100
|
|
|
|
9
|
if (not $valid_permissions{$permission_type}) { |
|
194
|
1
|
|
|
|
|
167
|
croak "Wrong permission type: '$permission_type'. Valid permissions types: " . join(' ', keys %valid_permissions); |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
#Check role in param |
|
198
|
2
|
50
|
|
|
|
32
|
if (not $valid_roles{$role}) { |
|
199
|
0
|
|
|
|
|
0
|
croak "Wrong role: '$role'. Valid roles: " . join(' ', keys %valid_roles); |
|
200
|
|
|
|
|
|
|
} |
|
201
|
2
|
|
|
|
|
11
|
my $access_token = $self->__getAccessToken(); |
|
202
|
|
|
|
|
|
|
|
|
203
|
2
|
|
|
|
|
27
|
my $uri = URI->new(join('/', $FILE_API_URL, $file_id, 'permissions')); |
|
204
|
2
|
|
|
|
|
274
|
my $headers = [ |
|
205
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
206
|
|
|
|
|
|
|
'Content-Type' => 'application/json', |
|
207
|
|
|
|
|
|
|
]; |
|
208
|
2
|
|
|
|
|
11
|
my $request_content = { |
|
209
|
|
|
|
|
|
|
'type' => $permission_type, |
|
210
|
|
|
|
|
|
|
'role' => $role, |
|
211
|
|
|
|
|
|
|
}; |
|
212
|
|
|
|
|
|
|
|
|
213
|
2
|
|
|
|
|
30
|
my $request = HTTP::Request->new('POST', $uri, $headers, encode_json($request_content)); |
|
214
|
|
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
372
|
my $response = $self->{ua}->request($request); |
|
216
|
2
|
|
|
|
|
1733043
|
my $response_code = $response->code(); |
|
217
|
2
|
50
|
|
|
|
27
|
if ($response_code != 200) { |
|
218
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
|
219
|
0
|
|
|
|
|
0
|
croak "Can't share file id: $file_id. Code: $response_code. Error message: $error_message"; |
|
220
|
|
|
|
|
|
|
} |
|
221
|
2
|
|
|
|
|
10
|
return decode_json($response->content()); |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub getFileMetadata { |
|
225
|
2
|
|
|
2
|
1
|
548
|
my ($self, %opt) = @_; |
|
226
|
2
|
|
33
|
|
|
11
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
|
227
|
2
|
|
|
|
|
10
|
my $access_token = $self->__getAccessToken(); |
|
228
|
|
|
|
|
|
|
|
|
229
|
2
|
|
|
|
|
28
|
my $uri = URI->new(join('/', $FILE_API2_URL, $file_id)); |
|
230
|
2
|
|
|
|
|
239
|
$uri->query_form('supportsTeamDrives' => 'true'); |
|
231
|
|
|
|
|
|
|
|
|
232
|
2
|
|
|
|
|
180
|
my $headers = [ |
|
233
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
234
|
|
|
|
|
|
|
]; |
|
235
|
2
|
|
|
|
|
16
|
my $request = HTTP::Request->new("GET", $uri, $headers); |
|
236
|
2
|
|
|
|
|
272
|
my $response = $self->{ua}->request($request); |
|
237
|
2
|
|
|
|
|
519518
|
my $response_code = $response->code(); |
|
238
|
2
|
50
|
|
|
|
27
|
if ($response_code != 200) { |
|
239
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
|
240
|
0
|
|
|
|
|
0
|
croak "Can't get metadata from file id: $file_id. Code: $response_code. Error message: $error_message"; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
2
|
|
|
|
|
8
|
return decode_json($response->content()); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub shareFile { |
|
246
|
1
|
|
|
1
|
1
|
1283
|
my ($self, %opt) = @_; |
|
247
|
1
|
|
33
|
|
|
5
|
my $file_id = $opt{-file_id} || croak "You must specify '-file_id' param"; |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
## Adding permissions to file |
|
250
|
1
|
|
|
|
|
7
|
my $permission = $self->setFilePermission( |
|
251
|
|
|
|
|
|
|
-file_id => $file_id, |
|
252
|
|
|
|
|
|
|
-type => 'anyone', |
|
253
|
|
|
|
|
|
|
-role => 'reader', |
|
254
|
|
|
|
|
|
|
); |
|
255
|
1
|
50
|
33
|
|
|
65
|
if ((not exists $permission->{type}) || ($permission->{type} ne 'anyone')) { |
|
256
|
0
|
|
|
|
|
0
|
croak "Can't set permission to anyone for file id: $file_id"; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
|
|
259
|
1
|
|
|
|
|
378
|
my $metadata = $self->getFileMetadata( -file_id => $file_id ); |
|
260
|
1
|
|
|
|
|
147
|
return $metadata->{webContentLink}; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub __createEmptyFile { |
|
264
|
1
|
|
|
1
|
|
5
|
my ($self, $source_file, $file_size, $parents) = @_; |
|
265
|
1
|
|
|
|
|
5
|
my $access_token = $self->__getAccessToken(); |
|
266
|
|
|
|
|
|
|
|
|
267
|
1
|
|
|
|
|
58
|
my $body = { |
|
268
|
|
|
|
|
|
|
'name' => basename($source_file), |
|
269
|
|
|
|
|
|
|
}; |
|
270
|
1
|
50
|
|
|
|
5
|
$body->{parents} = $parents if $parents; |
|
271
|
1
|
|
|
|
|
9
|
my $body_json = encode_json($body); |
|
272
|
|
|
|
|
|
|
|
|
273
|
1
|
|
|
|
|
12
|
my $uri = URI->new($UPLOAD_FILE_API_URL); |
|
274
|
1
|
|
|
|
|
114
|
$uri->query_form('upload_type' => 'resumable'); |
|
275
|
1
|
|
|
|
|
93
|
my $headers = [ |
|
276
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
277
|
|
|
|
|
|
|
'Content-Length' => length($body_json), |
|
278
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
279
|
|
|
|
|
|
|
'X-Upload-Content-Length' => $file_size, |
|
280
|
|
|
|
|
|
|
]; |
|
281
|
|
|
|
|
|
|
|
|
282
|
1
|
|
|
|
|
8
|
my $request = HTTP::Request->new('POST', $uri, $headers, $body_json); |
|
283
|
1
|
|
|
|
|
236
|
my $response = $self->{ua}->request($request); |
|
284
|
|
|
|
|
|
|
|
|
285
|
1
|
|
|
|
|
240571
|
my $response_code = $response->code(); |
|
286
|
1
|
50
|
|
|
|
13
|
if ($response_code != 200) { |
|
287
|
0
|
|
|
|
|
0
|
my $error_message = __readErrorMessageFromResponse($response); |
|
288
|
0
|
|
|
|
|
0
|
croak "Can't upload part of file. Code: $response_code. Error message: $error_message"; |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
|
|
291
|
1
|
50
|
|
|
|
5
|
my $location = $response->header('Location') or croak "Location header not defined"; |
|
292
|
|
|
|
|
|
|
|
|
293
|
1
|
|
|
|
|
64
|
return $location; |
|
294
|
|
|
|
|
|
|
} |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub __readErrorMessageFromResponse { |
|
297
|
0
|
|
|
0
|
|
0
|
my ($response) = @_; |
|
298
|
0
|
|
|
|
|
0
|
my $error_message = eval {decode_json($response->content)}; |
|
|
0
|
|
|
|
|
0
|
|
|
299
|
0
|
0
|
|
|
|
0
|
if ($error_message) { |
|
300
|
0
|
|
|
|
|
0
|
return $error_message->{error}->{message}; |
|
301
|
|
|
|
|
|
|
} |
|
302
|
0
|
|
|
|
|
0
|
return ''; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub __searchFile { |
|
308
|
3
|
|
|
3
|
|
9
|
my ($self, $q) = @_; |
|
309
|
|
|
|
|
|
|
|
|
310
|
3
|
|
|
|
|
12
|
my $access_token = $self->__getAccessToken(); |
|
311
|
|
|
|
|
|
|
|
|
312
|
3
|
|
|
|
|
16
|
my $headers = [ |
|
313
|
|
|
|
|
|
|
'Authorization' => 'Bearer ' . $access_token, |
|
314
|
|
|
|
|
|
|
]; |
|
315
|
|
|
|
|
|
|
|
|
316
|
3
|
|
|
|
|
27
|
my $uri = URI->new($FILE_API_URL); |
|
317
|
3
|
|
|
|
|
345
|
$uri->query_form('q' => $q); |
|
318
|
3
|
|
|
|
|
459
|
my $request = HTTP::Request->new('GET', |
|
319
|
|
|
|
|
|
|
$uri, |
|
320
|
|
|
|
|
|
|
$headers, |
|
321
|
|
|
|
|
|
|
); |
|
322
|
3
|
|
|
|
|
391
|
my $files = []; |
|
323
|
3
|
|
|
|
|
15
|
$self->__apiRequest($request, $files); |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
|
|
326
|
3
|
|
|
|
|
28
|
return $files; |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub __apiRequest { |
|
330
|
3
|
|
|
3
|
|
11
|
my ($self, $request, $files) = @_; |
|
331
|
|
|
|
|
|
|
|
|
332
|
3
|
|
|
|
|
15
|
my $response = $self->{ua}->request($request); |
|
333
|
3
|
|
|
|
|
1075245
|
my $response_code = $response->code; |
|
334
|
3
|
50
|
|
|
|
42
|
if ($response_code != 200) { |
|
335
|
0
|
|
|
|
|
0
|
croak "Wrong response code on search_file. Code: $response_code"; |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
3
|
|
|
|
|
15
|
my $json_res = decode_json($response->content); |
|
339
|
|
|
|
|
|
|
|
|
340
|
3
|
50
|
|
|
|
536
|
if (my $next_token = $json_res->{next_token}) { |
|
341
|
0
|
|
|
|
|
0
|
my $uri = $request->uri; |
|
342
|
0
|
|
|
|
|
0
|
$uri->query_form('next_token' => $next_token); |
|
343
|
0
|
|
|
|
|
0
|
$self->__apiRequest($request, $files); |
|
344
|
|
|
|
|
|
|
} |
|
345
|
3
|
|
|
|
|
6
|
push @$files, @{$json_res->{files}}; |
|
|
3
|
|
|
|
|
12
|
|
|
346
|
|
|
|
|
|
|
|
|
347
|
3
|
|
|
|
|
42
|
return 1; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
sub __getAccessToken { |
|
351
|
10
|
|
|
10
|
|
27
|
my ($self) = @_; |
|
352
|
|
|
|
|
|
|
|
|
353
|
10
|
|
|
|
|
33
|
my $oauth = $self->{oauth}; |
|
354
|
|
|
|
|
|
|
my $token_info = |
|
355
|
10
|
|
|
|
|
19
|
eval { |
|
356
|
10
|
|
|
|
|
72
|
$oauth->getTokenInfo( -access_token => $self->{access_token} ); |
|
357
|
|
|
|
|
|
|
}; |
|
358
|
|
|
|
|
|
|
# If error on get token info or token is expired |
|
359
|
10
|
100
|
|
|
|
1181493
|
if (not $@) { |
|
360
|
9
|
50
|
33
|
|
|
102
|
if ((exists $token_info->{expires_in}) && ($token_info->{expires_in} > 5)) { |
|
361
|
9
|
|
|
|
|
58
|
return $self->{access_token}; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
#Refresh token |
|
366
|
1
|
|
|
|
|
9
|
$oauth->refreshToken( -refresh_token => $self->{refresh_token} ); |
|
367
|
1
|
|
|
|
|
122191
|
$self->{refresh_token} = $oauth->getRefreshToken(); |
|
368
|
1
|
|
|
|
|
8
|
$self->{access_token} = $oauth->getAccessToken(); |
|
369
|
|
|
|
|
|
|
|
|
370
|
1
|
|
|
|
|
7
|
return $self->{access_token}; |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
1; |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=encoding utf8 |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head1 NAME |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
B - simple Google drive API module |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
This module use to upload, download, share file on Google drive |
|
384
|
|
|
|
|
|
|
use Net::Google::Drive; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
#Create disk object. You need send in param 'access_token', 'refresh_token', 'client_id' and 'client_secret'. |
|
387
|
|
|
|
|
|
|
#Values of 'client_id' and 'client_secret' uses to create Net::Google::OAuth object so that update value of 'access_token'. |
|
388
|
|
|
|
|
|
|
my $disk = Net::Google::Drive->new( |
|
389
|
|
|
|
|
|
|
-client_id => $client_id, |
|
390
|
|
|
|
|
|
|
-client_secret => $client_secret, |
|
391
|
|
|
|
|
|
|
-access_token => $access_token, |
|
392
|
|
|
|
|
|
|
-refresh_token => $refresh_token, |
|
393
|
|
|
|
|
|
|
); |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# Search file by name |
|
396
|
|
|
|
|
|
|
my $file_name = 'upload.doc'; |
|
397
|
|
|
|
|
|
|
my $files = $disk->searchFileByName( -filename => $file_name ) or croak "File '$file_name' not found"; |
|
398
|
|
|
|
|
|
|
my $file_id = $files->[0]->{id}; |
|
399
|
|
|
|
|
|
|
print "File id: $file_id\n"; |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
#Download file |
|
402
|
|
|
|
|
|
|
my $dest_file = '/home/upload.doc'; |
|
403
|
|
|
|
|
|
|
$disk->downloadFile( |
|
404
|
|
|
|
|
|
|
-file_id => $file_id, |
|
405
|
|
|
|
|
|
|
-dest_file => $dest_file, |
|
406
|
|
|
|
|
|
|
); |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
#Upload file |
|
409
|
|
|
|
|
|
|
my $source_file = '/home/upload.doc'; |
|
410
|
|
|
|
|
|
|
my $res = $disk->uploadFile( -source_file => $source_file ); |
|
411
|
|
|
|
|
|
|
print "File: $source_file uploaded. File id: $res->{id}\n"; |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head1 METHODS |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=head2 new(%opt) |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
Create L object |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
%opt: |
|
420
|
|
|
|
|
|
|
-client_id => Your app client id (Get from google when register your app) |
|
421
|
|
|
|
|
|
|
-client_secret => Your app client secret (Get from google when register your app) |
|
422
|
|
|
|
|
|
|
-access_token => Access token value (Get from L) |
|
423
|
|
|
|
|
|
|
-refresh_token => Refresh token value (Get from L) |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head2 searchFileByName(%opt) |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
Search file on google disk by name. Return arrayref to info with found files. If files not found - return empty arrayref |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
%opt: |
|
430
|
|
|
|
|
|
|
-filename => Name of file to find |
|
431
|
|
|
|
|
|
|
Return: |
|
432
|
|
|
|
|
|
|
[ |
|
433
|
|
|
|
|
|
|
[0] { |
|
434
|
|
|
|
|
|
|
id "1f13sLfo6UEyUuFpn-NWPnY", |
|
435
|
|
|
|
|
|
|
kind "drive#file", |
|
436
|
|
|
|
|
|
|
mimeType "application/x-perl", |
|
437
|
|
|
|
|
|
|
name "drive.t" |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
] |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 searchFileByNameContains(%opt) |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
Search files on google drive by name contains value in param '-filename' |
|
444
|
|
|
|
|
|
|
Param and return value same as in method L |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
=head2 downloadFile(%opt) |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
Download file from google dist to -dest_file on local system. Return 1 if success, die in otherwise |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
%opt: |
|
451
|
|
|
|
|
|
|
-dest_file => Name of file on disk in which you will download file from google disk |
|
452
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 deleteFile(%opt) |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Delete file from google disk. Return 1 if success, die in otherwise |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
%opt: |
|
459
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 uploadFile(%opt) |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
Upload file from local system to google drive. Return file_info hashref if success, die in otherwise |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
%opt: |
|
466
|
|
|
|
|
|
|
-source_file => File on local system |
|
467
|
|
|
|
|
|
|
-parents => Optional arrayref of parent ids |
|
468
|
|
|
|
|
|
|
Return: |
|
469
|
|
|
|
|
|
|
{ |
|
470
|
|
|
|
|
|
|
id "1LVAr2PpqX9m314JyZ6YJ4v_KIzG0Gey2", |
|
471
|
|
|
|
|
|
|
kind "drive#file", |
|
472
|
|
|
|
|
|
|
mimeType "application/octet-stream", |
|
473
|
|
|
|
|
|
|
name "gogle_upload_file" |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
=head2 setFilePermission(%opt) |
|
477
|
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
Set permissions for file on google drive. Return permission hashref, die in otherwise |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
%opt: |
|
481
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
|
482
|
|
|
|
|
|
|
-type => The type of the grantee. Valid values are: (user, group, domain, anyone) |
|
483
|
|
|
|
|
|
|
-role => The role granted by this permission. Valid values are: (owner, organizer, fileOrganizer, writer, commenter, reader) |
|
484
|
|
|
|
|
|
|
Return: |
|
485
|
|
|
|
|
|
|
{ |
|
486
|
|
|
|
|
|
|
allowFileDiscovery JSON::PP::Boolean { |
|
487
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
|
488
|
|
|
|
|
|
|
public methods (0) |
|
489
|
|
|
|
|
|
|
private methods (0) |
|
490
|
|
|
|
|
|
|
internals: 0 |
|
491
|
|
|
|
|
|
|
}, |
|
492
|
|
|
|
|
|
|
id "anyoneWithLink", |
|
493
|
|
|
|
|
|
|
kind "drive#permission", |
|
494
|
|
|
|
|
|
|
role "reader", |
|
495
|
|
|
|
|
|
|
type "anyone" |
|
496
|
|
|
|
|
|
|
} |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 getFileMetadata(%opt) |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Get metadata of file. Return hashref with metadata if success, die in otherwise |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
%opt: |
|
503
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
|
504
|
|
|
|
|
|
|
Return: |
|
505
|
|
|
|
|
|
|
{ alternateLink "https://drive.google.com/file/d/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/view?usp=drivesdk", |
|
506
|
|
|
|
|
|
|
appDataContents JSON::PP::Boolean { |
|
507
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
|
508
|
|
|
|
|
|
|
public methods (0) |
|
509
|
|
|
|
|
|
|
private methods (0) |
|
510
|
|
|
|
|
|
|
internals: 0 |
|
511
|
|
|
|
|
|
|
}, |
|
512
|
|
|
|
|
|
|
capabilities { |
|
513
|
|
|
|
|
|
|
canCopy JSON::PP::Boolean { |
|
514
|
|
|
|
|
|
|
Parents Types::Serialiser::BooleanBase |
|
515
|
|
|
|
|
|
|
public methods (0) |
|
516
|
|
|
|
|
|
|
private methods (0) |
|
517
|
|
|
|
|
|
|
internals: 1 |
|
518
|
|
|
|
|
|
|
}, |
|
519
|
|
|
|
|
|
|
canEdit var{capabilities}{canCopy} |
|
520
|
|
|
|
|
|
|
}, |
|
521
|
|
|
|
|
|
|
copyable var{capabilities}{canCopy}, |
|
522
|
|
|
|
|
|
|
copyRequiresWriterPermission var{appDataContents}, |
|
523
|
|
|
|
|
|
|
createdDate "2018-10-04T12:05:15.896Z", |
|
524
|
|
|
|
|
|
|
downloadUrl "https://doc-0g-7o-docs.googleusercontent.com/docs/securesc/ck8i7vfbvef13kb30b8mkrcjv4ihp2uj/3mfn1kbr655euhlo7tctg5mmn8oirg |
|
525
|
|
|
|
|
|
|
gf/1538654400000/10526805100525201667/10526805100525201667/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut?e=download&gd=true", |
|
526
|
|
|
|
|
|
|
editable var{capabilities}{canCopy}, |
|
527
|
|
|
|
|
|
|
embedLink "https://drive.google.com/file/d/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/preview?usp=drivesdk", |
|
528
|
|
|
|
|
|
|
etag ""omwGuTP8OdxhZkubyp-j43cFdJQ/MTUzODY1NDcxNTg5Ng"", |
|
529
|
|
|
|
|
|
|
explicitlyTrashed var{appDataContents}, |
|
530
|
|
|
|
|
|
|
fileExtension "", |
|
531
|
|
|
|
|
|
|
fileSize 1000000, |
|
532
|
|
|
|
|
|
|
headRevisionId "0B4HgPHxdPy22UmZXSFVRTkRLbXhFakdzZjFSUGkrNWZIVFN3PQ", |
|
533
|
|
|
|
|
|
|
iconLink "https://drive-thirdparty.googleusercontent.com/16/type/application/octet-stream", |
|
534
|
|
|
|
|
|
|
id "10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut", |
|
535
|
|
|
|
|
|
|
kind "drive#file", |
|
536
|
|
|
|
|
|
|
labels { |
|
537
|
|
|
|
|
|
|
hidden var{appDataContents}, |
|
538
|
|
|
|
|
|
|
restricted var{appDataContents}, |
|
539
|
|
|
|
|
|
|
starred var{appDataContents}, |
|
540
|
|
|
|
|
|
|
trashed var{appDataContents}, |
|
541
|
|
|
|
|
|
|
viewed var{appDataContents} |
|
542
|
|
|
|
|
|
|
}, |
|
543
|
|
|
|
|
|
|
lastModifyingUser { |
|
544
|
|
|
|
|
|
|
displayName "Ларри Уолл", |
|
545
|
|
|
|
|
|
|
emailAddress "perlgogledrivemodule@gmail.com", |
|
546
|
|
|
|
|
|
|
isAuthenticatedUser var{capabilities}{canCopy}, |
|
547
|
|
|
|
|
|
|
kind "drive#user", |
|
548
|
|
|
|
|
|
|
permissionId 10526805100525201667 |
|
549
|
|
|
|
|
|
|
}, |
|
550
|
|
|
|
|
|
|
lastModifyingUserName "Ларри Уолл", |
|
551
|
|
|
|
|
|
|
markedViewedByMeDate "1970-01-01T00:00:00.000Z", |
|
552
|
|
|
|
|
|
|
md5Checksum "ded2a2983b3e1743152d8224549510e1", |
|
553
|
|
|
|
|
|
|
mimeType "application/octet-stream", |
|
554
|
|
|
|
|
|
|
modifiedByMeDate "2018-10-04T12:05:15.896Z", |
|
555
|
|
|
|
|
|
|
modifiedDate "2018-10-04T12:05:15.896Z", |
|
556
|
|
|
|
|
|
|
originalFilename "gogle_upload_file", |
|
557
|
|
|
|
|
|
|
ownerNames [ |
|
558
|
|
|
|
|
|
|
[0] "Ларри Уолл" |
|
559
|
|
|
|
|
|
|
], |
|
560
|
|
|
|
|
|
|
owners [ |
|
561
|
|
|
|
|
|
|
[0] { |
|
562
|
|
|
|
|
|
|
displayName "Ларри Уолл", |
|
563
|
|
|
|
|
|
|
emailAddress "perlgogledrivemodule@gmail.com", |
|
564
|
|
|
|
|
|
|
isAuthenticatedUser var{capabilities}{canCopy}, |
|
565
|
|
|
|
|
|
|
kind "drive#user", |
|
566
|
|
|
|
|
|
|
permissionId 10526805100525201667 |
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
], |
|
569
|
|
|
|
|
|
|
parents [ |
|
570
|
|
|
|
|
|
|
[0] { |
|
571
|
|
|
|
|
|
|
id "0AIHgPHxdPy22Uk9PVA", |
|
572
|
|
|
|
|
|
|
isRoot var{capabilities}{canCopy}, |
|
573
|
|
|
|
|
|
|
kind "drive#parentReference", |
|
574
|
|
|
|
|
|
|
parentLink "https://www.googleapis.com/drive/v2/files/0AIHgPHxdPy22Uk9PVA", |
|
575
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/parents/0AIHgPHxdPy22Uk9PVA" |
|
576
|
|
|
|
|
|
|
} |
|
577
|
|
|
|
|
|
|
], |
|
578
|
|
|
|
|
|
|
quotaBytesUsed 1000000, |
|
579
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut", |
|
580
|
|
|
|
|
|
|
shared var{appDataContents}, |
|
581
|
|
|
|
|
|
|
spaces [ |
|
582
|
|
|
|
|
|
|
[0] "drive" |
|
583
|
|
|
|
|
|
|
], |
|
584
|
|
|
|
|
|
|
title "gogle_upload_file", |
|
585
|
|
|
|
|
|
|
userPermission { |
|
586
|
|
|
|
|
|
|
etag ""omwGuTP8OdxhZkubyp-j43cFdJQ/N52l-iUAo-dARaTch8nQXOzl348"", |
|
587
|
|
|
|
|
|
|
id "me", |
|
588
|
|
|
|
|
|
|
kind "drive#permission", |
|
589
|
|
|
|
|
|
|
role "owner", |
|
590
|
|
|
|
|
|
|
selfLink "https://www.googleapis.com/drive/v2/files/10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut/permissions/me", |
|
591
|
|
|
|
|
|
|
type "user" |
|
592
|
|
|
|
|
|
|
}, |
|
593
|
|
|
|
|
|
|
version 2, |
|
594
|
|
|
|
|
|
|
webContentLink "https://drive.google.com/uc?id=10Z5YDCHn3gnj0S4_Lf0poc2Lm5so0Sut&export=download", |
|
595
|
|
|
|
|
|
|
writersCanShare var{capabilities}{canCopy} |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=head2 shareFile(%opt) |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
Share file for download. Return download link if success, die in otherwise |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
%opt: |
|
603
|
|
|
|
|
|
|
-file_id => Id of file on google disk |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head1 DEPENDENCE |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
L, L, L, L, L, L |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=head1 AUTHORS |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=over 4 |
|
612
|
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item * |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Pavel Andryushin |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=back |
|
618
|
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
620
|
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
This software is copyright (c) 2018 by Pavel Andryushin. |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
624
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
=cut |