Поступил ко мне небольшой заказ на автоматизацию заливки файлов на файлообменник Keep2Share.cc Всего было у меня 2 попытки делания данного проекта. Сначала опишу неудачную попытку, а в другом посте удачную. Задача стояла таким образом, чтобы сделать заливку напрямую из Delphi программы на сервере Keep2Share.cc
К счастью, у Keep2Share.cc есть API. Это API работает прекрасно со скриптами PHP, о чем приводится пример на той же странице с API.
Вот моя попытка, скажу сразу, удалось почти всё, кроме последнего шага, который мне показался самым трудным.
Согласно API все транзакции проходят в JSON формате, я научился получать токен авторизации, отправлять тестовый запрос, а вот загружать файл не научился, хотя удалось получить от сервера всю необходимую информацию для отправки файла на сервер. Проблема оказалась в том, что я не смог до конца корректно перевести CURL запрос в INDY. cURL я так понимаю это библиотека, аналогичная InDY, только для C подобных языков.
Получение токена авторизации
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
function TForm1.GetAuthtoken:string; var JSONObject:TJSONObject; ServerAnswer: string; JsonString: string; ss:TStringStream; JSONObjectAfter: TJSONObject; i: Integer; JsonValue: string; begin idHTTP.Request.Referer := 'http://keep2share.cc/api/v2/login'; idHTTP.Request.ContentType :='application/json'; JSONObject:=TJSONObject.Create; JSONObject.AddPair( (TJSONPair.Create('username','panteleevstas@gmail.com' )) ); JSONObject.AddPair( (TJSONPair.Create('password','SLwA63' )) ); JsonString:=JSONObject.ToString; ss:=TStringStream.Create(JsonString); ServerAnswer:=idHTTP.Post('http://keep2share.cc/api/v2/login', ss); //Memo.Lines.Add(ServerAnswer); // was for test noe commented JSONObjectAfter:=TJSONObject.ParseJSONValue(ServerAnswer) as TJSONObject; Result:=JSONObjectAfter.Pairs[2].JsonValue.ToString;; Result:=Result.Substring(1,Result.Length-2); FreeAndNil(JSONObject); end; |
Тестовый запрос на сервер
В API есть тестовый запрос на сервер
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
procedure TForm1.Test2(Sender: TObject); var JSONObject:TJSONObject; ServerAnswer: string; JsonString: string; ss:TStringStream; begin ss:=TStringStream.Create; idHTTP.Request.Referer := 'http://keep2share.cc/api/v2/test'; //idHTTP.Request.ContentType:='multipart/form-data'; //idHTTP.Request.ContentType:='application/json'; idHTTP.Request.ContentType:='application/www-x-form-urlencoded'; // <<< key element in code IdHTTP.Request.UserAgent:='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36'; JSONObject:=TJSONObject.Create; JSONObject.AddPair( (TJSONPair.Create('auth_token',GetAuthtoken )) ); JsonString:=JSONObject.ToString; ss:=TStringStream.Create(JsonString); ServerAnswer:=idHTTP.Post('http://keep2share.cc/api/v2/test', ss); Memo.Lines.Add(ServerAnswer); FreeAndNil(JSONObject); end; |
Попытка загрузить файл напрямую
Тут суть такова – отправляем запрос getUploadFormData – это запрос на получение данных от сервера. Далее по этим данным строим новый запрос.
По сути, нужно переписать PHP код из API на Delphi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public function uploadFile($file, $parent_id = null) { if(!is_file($file)) throw new Exception("File '{$file}' is not found"); $data = $this->getUploadFormData($parent_id); if($data['status'] == 'success') { $curl = curl_init(); $postFields = $data['form_data']; $postFields[$data['file_field']] = new CURLFile($file); curl_setopt_array($curl, array( CURLOPT_FOLLOWLOCATION => false, CURLOPT_RETURNTRANSFER => true, CURLOPT_URL => $data['form_action'], CURLOPT_POST => true, CURLOPT_POSTFIELDS =>$postFields, )); $response = curl_exec($curl); if($this->verbose) echo '<<', $response, "\n"; return json_decode($response); } else { self::log('Error uploading file : ' . print_r($data, true), 'error'); return false; } } |
Если смотреть в HTTPAnalyzer, то при попытке отправить файл происходит вот что
Я попытался повторить это в Delphi, но где-то не дожал с параметрами. Так как сервер ругается именно на неверные параметры. Кто пойдет по моим следам, если Вам нужна будет прямая загрузка из Delphi – можете просто продолжить и довести до конечного результата.
У меня же конечный результат есть, но я сделал несколько по другому, о чем напишу в следующем посте.
Я здесь оставлю черновой код, как есть. Тут всякие лишние переменные, не все убирается из памяти в конце, но это лишь потому, что я пытался достичь конечного результата и вся энергия уходила на это. Код я прибираю обычно по достижении результата.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
procedure TForm1.bUploadFormDataClick(Sender: TObject); var URL:string; JSONObject: TJSONObject; FormDataJO: TJSONObject; ParamsJO: TJSONObject; params: string; project: string; expire: string; extras: string; extrasJO: TJSONObject; projectName: string; UserID: string; ParentID: string; ParamsJOToSend: TJSONObject; ExtrasJOToSend: TJSONObject; ParamsJOToSendString: string; ajax: boolean; signature: string; qquuid: string; newguid: TGUID; PostData:TIdMultiPartFormDataStream; ajaxstring: string; qqtotalfilesize: string; ServerResponse:string; JsonPair:TJSONPair; begin // PostData:=TIdMultiPartFormDataStream.Create; JSONObject:=TJSONObject.ParseJSONValue(GetUploadFormData) as TJSONObject; URL:=JSONObject.Pairs[2].JsonValue.ToString; URL:=ExcludeDoubleQuotes(URL); // ajax:=FormDataJO.Pairs[0].JsonValue.ToString; FormDataJO:= JSONObject.GetValue('form_data') as TJSONObject; ajax:=FormDataJO.GetValue('ajax') is TJSONTrue; if ajax then ajaxstring:='true' else // ajaxstring:='false'; if ajax then Memo.Lines.Add('ajax=true') else // Memo.Lines.Add('ajax=false'); // Memo.Lines.Add(ajax); params:= FormDataJO.Pairs[1].JsonValue.ToString; params:=StringReplace(params,'\','',[rfReplaceAll]); params:=params.Substring(1,params.Length-2); //params:=ExcludeDoubleQuotes(params); Memo.Lines.Add( ' '); Memo.Lines.Add(params); ParamsJO:=TJSONObject.ParseJSONValue(params) as TJSONObject; project:=ParamsJO.Pairs[0].JsonValue.ToString; //project:=project.Substring(1,project.Length-2); project:=ExcludeDoubleQuotes(project); expire:=ParamsJO.Pairs[1].JsonValue.ToString; //expire:=project.Substring(1,project.Length-2); expire:=ExcludeDoubleQuotes(expire); extras:=ParamsJO.Pairs[2].JsonValue.ToString; extras:=StringReplace(extras,'\','',[rfReplaceAll ]); //extras:=params.Substring(1,extras.Length-2); extrasJO:=TJSONObject.ParseJSONValue(extras) as TJSONObject; projectName:=extrasJO.Pairs[2].JsonValue.ToString; // projectName:=projectName.Substring(1,projectName.Length-2); projectName:=ExcludeDoubleQuotes(projectName); Memo.Lines.Add('projectName='+projectName); UserID:=extrasJO.Pairs[0].JsonValue.ToString; //UserID:=project.Substring(1,UserID.Length-2); UserID:=ExcludeDoubleQuotes(UserID); Memo.Lines.Add('UserID='+UserID); ParentID:=extrasJO.Pairs[1].JsonValue.ToString; ParentID:=ExcludeDoubleQuotes(ParentID); if ParentID='' then ParentID:='null'; Memo.Lines.Add('ParentID='+ParentID); //BuildingParamsString ParamsJOToSend:=TJSONObject.Create; ParamsJOToSend.AddPair( (TJSONPair.Create('node_name','node-12' )) ); ParamsJOToSend.AddPair( (TJSONPair.Create('project',project )) ); ParamsJOToSend.AddPair( (TJSONPair.Create('expire',expire )) ); ExtrasJOToSend:=TJSONObject.Create; //{"project_name":"k2s","user_id":1444220,"parent_id":null} ExtrasJOToSend.AddPair( (TJSONPair.Create('project_name',projectName )) ); ExtrasJOToSend.AddPair( (TJSONPair.Create('user_id',UserID )) ); ExtrasJOToSend.AddPair( (TJSONPair.Create('parent_id',ParentID )) ); //ShowMessage(ExtrasJOToSend.ToString); ParamsJOToSend.AddPair( (TJSONPair.Create('extras',ExtrasJOToSend.ToString )) ); ParamsJOToSendString:=StringReplace(ParamsJOToSend.ToString,'\','',[rfReplaceAll ]); //ParamsJOToSendString:=StringReplace(ParamsJOToSend.ToString,'""','null',[rfReplaceAll ]); Memo.Lines.Add(ParamsJOToSendString); //signature signature:=FormDataJO.GetValue('signature').Value; Memo.Lines.Add('signature='+signature); //qquuid //5ca2cf49-607e-4114-8be8-4883bf4b63d4 //44008104-519F-4106-A837-6F0ADF7DBA72 //guid Createguid(newguid); qquuid:=newguid.ToString; qquuid:=qquuid.Substring(1,qquuid.Length-2); ShowMessage(qquuid); Memo.Lines.Add(qquuid); if OpenDialog.Execute then begin qqtotalfilesize:=GetFileSize(OpenDialog.FileName).ToString; // Sending Post Request and File PostData.AddFormField('signature',signature); PostData.AddFormField('params',ParamsJOToSendString); PostData.AddFormField('ajax',ajaxstring); PostData.AddFormField('qquuid',qquuid); PostData.AddFormField('qqtotalfilesize',qqtotalfilesize); PostData.AddFile('file', OpenDialog.FileName, 'image/jpeg'); {//idHTTP.Request.ContentType :='application/json'; idHTTP.Request.ContentType :='multipart/form-data'; } IdHTTP.Request.ContentEncoding:=''; ServerResponse:=IdHTTP.Post(URL,PostData); Memo.Lines.Add(' ServerResponse '); Memo.Lines.Add(ServerResponse); end; // Freeing FreeAndNil(PostData); end; |
В общем стопорнулся в вот на этом…
Посидев какое-то время на этой проблеме, я пришел к выводу, что в моих условиях есть более простой и изящный путь – загрузить все по HTTP протоколу на VPS сервер, на котором установлен IIS с PHP и уже собственно оттуда загружать на k2s.