Advertisement
jabounet

TgoogleDrive

Dec 31st, 2016
102
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pascal 15.20 KB | None | 0 0
  1. unit google_drive;
  2.  
  3. {$IFDEF FPC}
  4.   {$mode objfpc}{$H+}
  5. {$ENDIF}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, DB, Forms, google_oauth2, fpjson, jsonparser, memds,
  11.   httpsend, blcksock, typinfo, comctrls, synautil, StdCtrls;
  12.  
  13. type
  14.   TGoogleDrive = class(TMemDataSet)
  15.   private
  16.     { private declarations }
  17.     FgOAuth2: TGoogleOAuth2;
  18.     LastErrorCode: string;
  19.     LastErrorMessage: string;
  20.     Bytes : Integer;
  21.     MaxBytes : Integer;
  22.     downHTTP: THTTPSend;
  23.     FLogMemo: TMemo;
  24.     FDebugMemo: TMemo;
  25.     FProgress: TProgressBar;
  26.     procedure DownStatus(Sender: TObject; Reason: THookSocketReason; const Value: String);
  27.     function GetSizeFromHeader(Header: String):integer;
  28.     procedure UpStatus(Sender: TObject; Reason: THookSocketReason; const Value: string);
  29.   protected
  30.     { protected declarations }
  31.   public
  32.     { public declarations }
  33.     constructor Create(AOwner: TComponent; client_id, client_secret: string); overload;
  34.     destructor Destroy; override;
  35.  
  36.     procedure Populate(aFilter: string = '');
  37.     function DownloadFile(id,TargetFile: string): Boolean;
  38.     function GetUploadURI(const URL, auth, FileN, Description: string; const Data: TStream): string;
  39.     property gOAuth2: TGoogleOAuth2 read FgOAuth2 write FgOAuth2;
  40.     function UploadResumableFile(const URL: string; const Data: TStream): string;
  41.     property Progress: TProgressBar read Fprogress write Fprogress;
  42.     property LogMemo: TMemo read FLogMemo write FLogMemo;
  43.     property DebugMemo: TMemo read FDebugMemo write FDebugMemo;
  44.  
  45.   published
  46.   end;
  47.  
  48.  
  49. implementation
  50.  
  51.  
  52. procedure TGoogleDrive.UpStatus(Sender: TObject; Reason: THookSocketReason; const Value: string);
  53. begin
  54.   if Reason = HR_WriteCount then
  55.   begin
  56.     Progress.StepBy(StrToIntDef(Value, 0));
  57.     Application.ProcessMessages;
  58.   end;
  59. end;
  60.  
  61. function TGoogleDrive.UploadResumableFile(const URL: string; const Data: TStream): string;
  62. const
  63.   MaxChunk = 40 * 256 * 1024; // ALWAYS chunks of 256KB
  64. var
  65.   HTTP: THTTPSend;
  66.   s: string;
  67.   i: integer;
  68.   From, Size: integer;
  69.   Tries, PrevFrom: integer;
  70. begin
  71.   Result := '';
  72.   HTTP := THTTPSend.Create;
  73.   try
  74.     // Always check if there already was aborted upload (is easiest)
  75.     HTTP.Headers.Add('Content-Length: 0');
  76.     HTTP.Headers.Add('Content-Range: bytes */*');
  77.  
  78.     if not HTTP.HTTPMethod('PUT', URL) then exit;
  79.     Result := 'pre - ' + #13 + HTTP.Headers.Text + #13 + #13 + HTTP.ResultString; // for any errors
  80.     // Mainform.Memo2.Lines.Add('@@@'+Result);
  81.     From := 0;
  82.     if HTTP.ResultCode in [200, 201] then
  83.     begin
  84.       Result := '200 already uploaded completely';
  85.       exit;
  86.     end;
  87.     if HTTP.ResultCode = 308 then // Resume Incomplete
  88.     begin
  89.       for i := 0 to HTTP.Headers.Count - 1 do
  90.       begin
  91.         if Pos('Range: bytes=0-', HTTP.Headers.Strings[i]) > 0 then
  92.         begin
  93.           s := StringReplace(HTTP.Headers.Strings[i], 'Range: bytes=0-', '', []);
  94.           From := StrToIntDef(s, -1) + 1; // from 0 or max_range + 1
  95.           break;
  96.         end;
  97.       end;
  98.     end;
  99.     if not HTTP.ResultCode in [200, 201, 308] then exit;
  100.  
  101.     Tries := 0;
  102.     PrevFrom := From;
  103.     Progress.Min := 0;
  104.     Progress.Max := Data.Size - 1;
  105.     HTTP.Sock.OnStatus := @UpStatus;
  106.     repeat
  107.  
  108.       Progress.Position := From;
  109.  
  110.       HTTP.Document.Clear;
  111.       HTTP.Headers.Clear;
  112.  
  113.       // We need to resune upload from position "from"
  114.       Data.Position := From;
  115.       Size := Data.Size - From;
  116.       if Size > MaxChunk then Size := MaxChunk;
  117.       HTTP.Document.CopyFrom(Data, Size);
  118.       HTTP.Headers.Add(Format('Content-Range: bytes %d-%d/%d', [From, From + Size - 1, Data.Size]));
  119.       HTTP.MimeType := '';
  120.       LogMemo.lines.Add(HTTP.Headers.Text);
  121.       if not HTTP.HTTPMethod('PUT', URL) then exit;
  122.  
  123.       Result := HTTP.Headers.Text + #13 + #13 + HTTP.ResultString;
  124.       // Mainform.Memo2.Lines.Add(Result);
  125.  
  126.       if HTTP.ResultCode in [200, 201] then Result := '200 Upload complete';
  127.       if HTTP.ResultCode = 308 then // Resume Incomplete
  128.       begin
  129.         for i := 0 to HTTP.Headers.Count - 1 do
  130.         begin
  131.           if Pos('Range: bytes=0-', HTTP.Headers.Strings[i]) > 0 then
  132.           begin
  133.             s := StringReplace(HTTP.Headers.Strings[i], 'Range: bytes=0-', '', []);
  134.             PrevFrom := From;
  135.             From := StrToIntDef(s, -1) + 1; // from 0 or max_range + 1
  136.             break;
  137.           end;
  138.         end;
  139.       end;
  140.  
  141.       // no 308 with actual transfer is received, increase tries
  142.       if PrevFrom = From then Inc(Tries);
  143.  
  144.     until (HTTP.ResultCode in [200, 201]) or (Tries > 1);
  145.  
  146.   finally
  147.     HTTP.Free;
  148.   end;
  149.  
  150. end;
  151.  
  152.  
  153. function TGoogleDrive.GetUploadURI(const URL, auth, FileN, Description: string; const Data: TStream): string;
  154. var
  155.   HTTP: THTTPSend;
  156.   s: string;
  157.   i: integer;
  158. begin
  159.   Result := '';
  160.   HTTP := THTTPSend.Create;
  161.   try
  162.     s := Format('{' + CRLF + '"name": "%s",' + CRLF + '"description": "%s"' + CRLF + '}',
  163.       [ExtractFileName(FileN), Description]);
  164.     WriteStrToStream(HTTP.Document, ansistring(s));
  165.     HTTP.Headers.Add('Authorization: Bearer ' + auth);
  166.     HTTP.Headers.Add(Format('X-Upload-Content-Length: %d', [Data.Size]));
  167.     HTTP.MimeType := 'application/json; charset=UTF-8';
  168.     if not HTTP.HTTPMethod('POST', URL) then exit;
  169.     Result := HTTP.ResultString; // for any errors
  170.     for i := 0 to HTTP.Headers.Count - 1 do
  171.     begin
  172.       if Pos('Location: ', HTTP.Headers.Strings[i]) > 0 then
  173.       begin
  174.         Result := StringReplace(HTTP.Headers.Strings[i], 'Location: ', '', []);
  175.         break;
  176.       end;
  177.     end;
  178.   finally
  179.     HTTP.Free;
  180.   end;
  181. end;
  182.  
  183.  
  184. function TGoogleDrive.DownloadFile(id,TargetFile: string): Boolean;
  185. var
  186.   HTTPGetResult: Boolean;
  187.   URL:string;
  188. begin
  189.   Result := False;
  190.   if gOAuth2.EMail = '' then exit;
  191.   Bytes:= 0;
  192.   MaxBytes:= -1;
  193.   DownHTTP := THTTPSend.Create;
  194.   try
  195.     DownHTTP.Sock.OnStatus:= @DownStatus;
  196.     URL:='https://www.googleapis.com/drive/v3/files/'+id+'?alt=media';
  197.     DownHTTP.Headers.Add('Authorization: Bearer ' + gOAuth2.Access_token);
  198.     result:=DownHTTP.HTTPMethod('GET', URL);
  199.     if (DownHTTP.ResultCode >= 100) and (DownHTTP.ResultCode<=299) then begin
  200.       DownHTTP.Document.SaveToFile(TargetFile);
  201.       Result := True;
  202.     end;
  203.   finally
  204.     DownHTTP.Free;
  205.   end;
  206. end;
  207.  
  208. procedure TGoogleDrive.DownStatus(Sender: TObject; Reason: THookSocketReason; const Value: String);
  209. var
  210.   V, currentHeader: String;
  211.   i: integer;
  212.   var pct:integer;
  213. begin
  214.   if (MaxBytes = -1) then
  215.   begin
  216.     for i:= 0 to DownHTTP.Headers.Count - 1 do
  217.     begin
  218.       currentHeader:= DownHTTP.Headers[i];
  219.       MaxBytes:= GetSizeFromHeader(currentHeader);
  220.       if MaxBytes <> -1 then break;
  221.     end;
  222.   end;
  223.  
  224.   V := GetEnumName(TypeInfo(THookSocketReason), Integer(Reason)) + ' ' + Value;
  225.  
  226.   if Reason = THookSocketReason.HR_ReadCount then
  227.   begin
  228.     Bytes:= Bytes + StrToInt(Value);
  229.     pct:=round(Bytes/maxbytes*100);
  230.     Progress.Position:=pct;
  231.     application.processmessages;
  232.   end;
  233. end;
  234.  
  235. function TGoogleDrive.GetSizeFromHeader(Header: String): integer;
  236. var
  237.   item : TStringList;
  238. begin
  239.   Result:= -1;
  240.  
  241.   if Pos('Content-Length:', Header) <> 0 then
  242.   begin
  243.     item:= TStringList.Create();
  244.     item.Delimiter:= ':';
  245.     item.StrictDelimiter:=true;
  246.     item.DelimitedText:=Header;
  247.     if item.Count = 2 then
  248.     begin
  249.       Result:= StrToInt(Trim(item[1]));
  250.     end;
  251.   end;
  252. end;
  253.  
  254.  
  255.  
  256.  
  257. constructor TGoogleDrive.Create(AOwner: TComponent; client_id, client_secret: string);
  258. begin
  259.   inherited Create(AOwner);
  260.   FieldDefs.Clear;
  261.   //FieldDefs.Add('Boolean', ftBoolean, 0, False);
  262.   //FieldDefs.Add('Integer', ftInteger, 0, False);
  263.   //FieldDefs.Add('SmallInt', ftSmallInt, 0, False);
  264.   //FieldDefs.Add('Float', ftFloat, 0, False);
  265.   //FieldDefs.Add('String', ftString, 30, False);
  266.   //FieldDefs.Add('Time', ftTime, 0, False);
  267.   //FieldDefs.Add('Date', ftDate, 0, False);
  268.   //FieldDefs.Add('DateTime', ftDateTime, 0, False);
  269.   FieldDefs.Add('title', ftString, 25, False);
  270.   FieldDefs.Add('fileId', ftString, 255, False);
  271.   FieldDefs.Add('description', ftString, 255, False);
  272.   FieldDefs.Add('created', ftString, 255, False);
  273.   FieldDefs.Add('modified', ftString, 255, False);
  274.   FieldDefs.Add('downloadurl', ftString, 255, False);
  275.   FieldDefs.Add('filename', ftString, 255, False);
  276.   FieldDefs.Add('md5', ftString, 255, False);
  277.   FieldDefs.Add('filesize', ftString, 20, False);
  278.   CreateTable;
  279.  
  280.   gOAuth2 := TGoogleOAuth2.Create(client_id, client_secret);
  281.  
  282. end;
  283.  
  284. destructor TGoogleDrive.Destroy;
  285. begin
  286.   gOAuth2.Free;
  287.   inherited Destroy;
  288. end;
  289.  
  290. function RetrieveJSONValue(JSON: TJSONData; Value: string): string;
  291. var
  292.   D: TJSONData;
  293. begin
  294.   Result := '';
  295.   if Assigned(JSON) then
  296.   begin
  297.     D := JSON.FindPath(Value);
  298.     if assigned(D) then
  299.       Result := D.AsString;
  300.   end;
  301. end;
  302.  
  303. procedure TGoogleDrive.Populate(aFilter: string = '');
  304. var
  305.   Response: TStringList;
  306.   URL: string;
  307.   Params: string;
  308.   P: TJSONParser;
  309.   I: integer;
  310.   J, D, E: TJSONData;
  311. begin
  312.   (*
  313.   {
  314.    "kind": "drive#fileList",
  315.    "etag": etag,
  316.    "selfLink": string,
  317.    "nextPageToken": string,
  318.    "nextLink": string,
  319.    "items": [ files Resource ]
  320.   }
  321.  
  322.   {
  323.     "kind": "drive#file",
  324.     "id": string,
  325.     "etag": etag,
  326.     "selfLink": string,
  327.     "webContentLink": string,
  328.     "webViewLink": string,
  329.     "alternateLink": string,
  330.     "embedLink": string,
  331.     "openWithLinks": {
  332.       (key): string
  333.     },
  334.     "defaultOpenWithLink": string,
  335.     "iconLink": string,
  336.     "thumbnailLink": string,
  337.     "thumbnail": {
  338.       "image": bytes,
  339.       "mimeType": string
  340.     },
  341.     "title": string,
  342.     "mimeType": string,
  343.     "description": string,
  344.     "labels": {
  345.       "starred": boolean,
  346.       "hidden": boolean,
  347.       "trashed": boolean,
  348.       "restricted": boolean,
  349.       "viewed": boolean
  350.     },
  351.     "createdDate": datetime,
  352.     "modifiedDate": datetime,
  353.     "modifiedByMeDate": datetime,
  354.     "lastViewedByMeDate": datetime,
  355.     "markedViewedByMeDate": datetime,
  356.     "sharedWithMeDate": datetime,
  357.     "version": long,
  358.     "sharingUser": {
  359.       "kind": "drive#user",
  360.       "displayName": string,
  361.       "picture": {
  362.         "url": string
  363.       },
  364.       "isAuthenticatedUser": boolean,
  365.       "permissionId": string,
  366.       "emailAddress": string
  367.     },
  368.     "parents": [
  369.       parents Resource
  370.     ],
  371.     "downloadUrl": string,
  372.     "downloadUrl": string,
  373.     "exportLinks": {
  374.       (key): string
  375.     },
  376.     "indexableText": {
  377.       "text": string
  378.     },
  379.     "userPermission": permissions Resource,
  380.     "permissions": [
  381.       permissions Resource
  382.     ],
  383.     "originalFilename": string,
  384.     "fileExtension": string,
  385.     "fullFileExtension": string,
  386.     "md5Checksum": string,
  387.     "fileSize": long,
  388.     "quotaBytesUsed": long,
  389.     "ownerNames": [
  390.       string
  391.     ],
  392.     "owners": [
  393.       {
  394.         "kind": "drive#user",
  395.         "displayName": string,
  396.         "picture": {
  397.           "url": string
  398.         },
  399.         "isAuthenticatedUser": boolean,
  400.         "permissionId": string,
  401.         "emailAddress": string
  402.       }
  403.     ],
  404.     "lastModifyingUserName": string,
  405.     "lastModifyingUser": {
  406.       "kind": "drive#user",
  407.       "displayName": string,
  408.       "picture": {
  409.         "url": string
  410.       },
  411.       "isAuthenticatedUser": boolean,
  412.       "permissionId": string,
  413.       "emailAddress": string
  414.     },
  415.     "ownedByMe": boolean,
  416.     "editable": boolean,
  417.     "canComment": boolean,
  418.     "canReadRevisions": boolean,
  419.     "shareable": boolean,
  420.     "copyable": boolean,
  421.     "writersCanShare": boolean,
  422.     "shared": boolean,
  423.     "explicitlyTrashed": boolean,
  424.     "appDataContents": boolean,
  425.     "headRevisionId": string,
  426.     "properties": [
  427.       properties Resource
  428.     ],
  429.     "folderColorRgb": string,
  430.     "imageMediaMetadata": {
  431.       "width": integer,
  432.       "height": integer,
  433.       "rotation": integer,
  434.       "location": {
  435.         "latitude": double,
  436.         "longitude": double,
  437.         "altitude": double
  438.       },
  439.       "date": string,
  440.       "cameraMake": string,
  441.       "cameraModel": string,
  442.       "exposureTime": float,
  443.       "aperture": float,
  444.       "flashUsed": boolean,
  445.       "focalLength": float,
  446.       "isoSpeed": integer,
  447.       "meteringMode": string,
  448.       "sensor": string,
  449.       "exposureMode": string,
  450.       "colorSpace": string,
  451.       "whiteBalance": string,
  452.       "exposureBias": float,
  453.       "maxApertureValue": float,
  454.       "subjectDistance": integer,
  455.       "lens": string
  456.     },
  457.     "videoMediaMetadata": {
  458.       "width": integer,
  459.       "height": integer,
  460.       "durationMillis": long
  461.     },
  462.     "spaces": [
  463.       string
  464.     ],
  465.     "isAppAuthorized": boolean
  466.   }
  467.  
  468.  
  469.  
  470.  
  471.   *)
  472.   Response := TStringList.Create;
  473.   Self.DisableControls;
  474.   try
  475.  
  476.     if gOAuth2.EMail = '' then exit;
  477.  
  478.     // https://developers.google.com/drive/v2/reference/files/list
  479.     gOAuth2.LogLine('Retrieving filelist ' + gOAuth2.EMail);
  480.     URL := 'https://www.googleapis.com/drive/v2/files';
  481.     Params := 'access_token=' + gOAuth2.Access_token;
  482.     Params := Params + '&maxResults=1000';
  483.     Params := Params + '&orderBy=folder,modifiedDate%20desc,title';
  484.     if HttpGetText(URL + '?' + Params, Response) then
  485.     begin
  486.       // gOAuth2.DebugLine(Response.Text);
  487.  
  488.       P := TJSONParser.Create(Response.Text);
  489.       try
  490.         J := P.Parse;
  491.         if Assigned(J) then
  492.         begin
  493.  
  494.           D := J.FindPath('error');
  495.           if assigned(D) then
  496.           begin
  497.             LastErrorCode := RetrieveJSONValue(D, 'code');
  498.             LastErrorMessage := RetrieveJSONValue(D, 'message');
  499.             gOAuth2.LogLine(format('Error %s: %s',
  500.               [LastErrorCode, LastErrorMessage]));
  501.             exit;
  502.           end;
  503.  
  504.           gOAuth2.LogLine('Busy filling dataset');
  505.  
  506.           D := J.FindPath('items');
  507.           gOAuth2.DebugLine(format('%d items received', [D.Count]));
  508.           for I := 0 to D.Count - 1 do
  509.           begin
  510.             Append;
  511.             // 2015-02-10T10:42:49.297Z
  512.             // 2012-05-18T15:45:00+02:00
  513.             FieldByName('title').AsString := RetrieveJSONValue(D.Items[I], 'title');
  514.             FieldByName('fileId').AsString := RetrieveJSONValue(D.Items[I], 'id');
  515.             FieldByName('description').AsString := RetrieveJSONValue(D.Items[I], 'description');
  516.             FieldByName('created').AsString := RetrieveJSONValue(D.Items[I], 'createdDate');
  517.             FieldByName('modified').AsString := RetrieveJSONValue(D.Items[I], 'modifiedDate');
  518.             FieldByName('downloadurl').AsString := RetrieveJSONValue(D.Items[I], 'downloadUrl');
  519.             FieldByName('filename').AsString := RetrieveJSONValue(D.Items[I], 'originalFilename');
  520.             FieldByName('md5').AsString := RetrieveJSONValue(D.Items[I], 'md5Checksum');
  521.             FieldByName('filesize').AsString := RetrieveJSONValue(D.Items[I], 'fileSize');
  522.             Self.Post;
  523.             Application.ProcessMessages;
  524.  
  525.           end;
  526.  
  527.           gOAuth2.LogLine(format('%d items stored', [Self.RecordCount]));
  528.  
  529.           gOAuth2.LogLine('Done filling dataset');
  530.  
  531.         end;
  532.       finally
  533.         if assigned(J) then
  534.           J.Free;
  535.         P.Free;
  536.       end;
  537.  
  538.     end;
  539.  
  540.   finally
  541.     Response.Free;
  542.     Self.EnableControls;
  543.   end;
  544.  
  545. end;
  546.  
  547. end.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement