среда, 5 июля 2017 г.

Delphi 10.2 Tokyo и доступ к Яндекс.Диск через REST API. Часть 2 - загрузка файла

В первой части мы прошли аутентификацию и получили токен. Теперь настало время перейти к следующей части - загрузке файлов на ЯндексДиск посредством REST API с использованием RESTClient и RESTRequest. В интернете много публикаций, в которых рассматривается чтение с Диска, но про загрузку что-то не встречал. Хотя все не просто, а очень просто ))

Берем предыдущий проект и добавляем на форму RESTClient и RESTRequest. В uses добавляем REST.Types, System.JSON.
RESTClient.BaseURL выставляем в https://cloud-api.yandex.net/v1 и удостоверяемся, что Authenticator подхватил OAuth2Authenticator1 с которым мы эксперементировали до этого.
Теперь - ВНИМАНИЕ!!! Очищаем свойство Authenticator у компонена RESTClient. Почему? Потому что эта [censored] подставляет токен в виде параметра access_token даже если ее не просят и при некоторых видах запросов портит Body.
В принципе, больше с ними ничего делать не надо, все остальное будем менять программно.
Так же можно добавить на форму Edit, SpeedButton и OpenDialog - для простоты выбора файла.  На SpeedButton вешаем обработчик

procedure TForm3.SpeedButton1Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
    Edit1.Text:=OpenDialog1.FileName;

    UploadFile(Edit1.Text);
  end;
end;

и собственно, процедура UploadFile, которая, используя полученный токен в два захода отправляет файл на Диск. Почему в два захода? Потому что первым заходом получает ссылку для загрузки, время жизни которой ограничено 30 минутами и для которой не требуется токен.

procedure TForm3.UploadFile(FileName: String);
var SFileName,Link,Mem:String;
    Code:Integer;
begin
  //выделяем только имя файла, без путей
  SFileName:=ExtractFileName(FileName);
  memo1.Lines.Add('uploading '+SFileName);
  //подготавливаем параметры
  RESTRequest1.Params.Clear;
  RESTRequest1.Params.Add;
  //в первую очередь дополнительный заголовок в запрос
  //с токеном, иначе будет 401 UNAUTHORIZED
  RESTRequest1.Params[0].Kind:=TRESTRequestParameterKind.pkHTTPHEADER;
  RESTRequest1.Params[0].name:='Authorization';
  RESTRequest1.Params[0].Options:=[poDoNotEncode];
  RESTRequest1.Params[0].Value:='OAuth ' + OAuth2Authenticator1.AccessToken;

  //далее - путь загрузки
  //делаем просто в папку приложения
  RESTRequest1.Params.Add;
  RESTRequest1.Params[1].name:='path';
  RESTRequest1.Params[1].Value:='app:/'+SFilename;
  //перезапишем без вопросов
  RESTRequest1.Params.Add;
  RESTRequest1.Params[2].name:='overwrite';
  RESTRequest1.Params[2].Value:='true';

  RESTRequest1.Resource:='/disk/resources/upload';
  RESTRequest1.Method:=rmGet;
  //ну и собственно запрос ссылки на загрузк
  RESTRequest1.Execute;
  if RESTRequest1.Response.StatusCode=200 then begin
    //все нормально, для отладки выведем полученный JSON
    Memo1.Lines.Add(RESTRequest1.Response.Content);

    //выделяем ссылку на загрузку
    Link:=TJSONObject(RESTRequest1.Response.JSONValue).GetValue('href').Value;
    Memo1.lines.add(Link);
   //запоминаем базовый путь
    Mem:=RESTClient1.BaseURL;

    try
      //прописываем ссылку на загрузку
      RESTClient1.BaseURL:=Link;

      //очищаем параметры, токен при это не нужен
      RESTRequest1.Params.Clear;
      RESTRequest1.Resource:='';
      //добавляем файл в запрос
      RESTRequest1.AddFile(FileName);
      //метод - PUT
      RESTRequest1.Method:=rmPUT;
      //ну и отправляем файл на сервер
      RESTRequest1.Execute;
    finally
      //после чего восстанавливаем ссылку на API
      RESTClient1.BaseURL:=Mem;
    end;
    Code:=RESTRequest1.Response.StatusCode;
    if Code=201 then Memo1.Lines.Add('файл успешно загружен');
    if Code=202 then Memo1.Lines.Add('файл загружен на сервер, но пока не передан в папку назначения');
    if Code=412 then Memo1.Lines.Add('при дозагрузке файла был передан неверный диапазон в заголовке Content-Range');
    if Code=413 then Memo1.Lines.Add('размер файла превышает 10Гб');
    if Code=500 then Memo1.Lines.Add('внутренняя ошибка сервера, попробуйте позже');
    if Code=503 then Memo1.Lines.Add('сервис недоступен, попробуйте позже');
    if Code=507 then Memo1.Lines.Add('исчерпано место на Диске');

  end else Memo1.Lines.Add('запрос ссылки - ошибка '+RESTRequest1.Response.StatusCode.ToString);
end;
 Вот и все! Для использования в реальных условиях, конечно, надо оборачивать вызов UploadFile в try..except, поскольку даже при написании этой статьи я словил таймаут, не говоря уж о прочих возможных ошибках.

UPD. Забыл! Для того, чтобы не спотыкаться об "401 UNAUTHORIZED",  крайне важно у компонента OAuth2Authenticator свойство TokenType выставить в  ttNONE, иначе при запросах в заголовке Authorization подставляется Bearer, из-за чего система и дает отлуп.


1 комментарий: