Link to home
Start Free TrialLog in
Avatar of danz67
danz67Flag for Italy

asked on

Delphi - DropBox

hello,
I state that I have activated everything you need on site dropbox.

Is there any example of how to upload and download files using delphi?

thanks
Avatar of MichaelStaszewski
MichaelStaszewski
Flag of United States of America image

I have a fully working Dropbox.pas unit that I wrote for my company that I'm using for my current project. Unfortunately I cannot provide it to you because it is owned by my company, but I can assist you if you have specific questions. I will outline the basics of my interface to get you started.

1. I use TIdHTTP to make the HTTP requests into their REST web service.
2. I use the open source OAuth.pas (Google it) for the OAuth; however, it has two major flaws. If you choose to go this route of writing your own interface and using OAuth.pas I can outline the bugs and what I had to do to correct them.
3. I use SuperObject JSON parser (Google it) to process the JSON results. I started my project under Delphi 2009 and am now on XE2. I understand that XE2 has JSON support, but I've stuck with SuperObject since it works well and I am familiar with it.

Those are the basic components that make up my application. You need to understand OAuth. The latest version of their API requires OAuth authentication including obtaining the request token, authorizing the user, and finally getting the access token. This is a critical step and there is a lot out there on the subject. You will find that many others will direct you to OAuth.net and tell you to read the info there. This is almost useless. It's like telling someone to read the entire manufacturer's service manual for their car when they ask how to change the oil. It's a much more complicated standard than what you really need to know for real world application, especially for communicating with Dropbox. Search OAuth and find some sites that outline the basic principles, then start playing with the API. Their dev forum is pretty good too.

Your HTTP requests must be formatted in a certain way to include both the OAuth parameters as well as any required for the particular API method you are using. The parameters, including the OAuth ones, are to be alpha sorted. This is one of the flaws of the OAuth.pas project. It does not alpha sort, it is easily corrected. Here is a sample call to get the metadata of a folder named test.

3/21/2012 2:39:28 PM

    GET: /1/metadata/sandbox/test?file_limit=10000&list=true&include_deleted=false&oauth_consumer_key=********************&oauth_nonce=07DE9EE58FE86DB711A77B8675E7183C&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1332358768&oauth_token=********************&oauth_version=1.0&oauth_signature=RTDYiCRIsXrB5yN%2Bkzs%2FMZX2KGo%3D

    Parameters:

    file_limit=10000
    list=true
    include_deleted=false
    oauth_consumer_key=********************
    oauth_nonce=07DE9EE58FE86DB711A77B8675E7183C
    oauth_signature_method=HMAC-SHA1
    oauth_timestamp=1332358768
    oauth_token=********************
    oauth_version=1.0
    oauth_signature=RTDYiCRIsXrB5yN%2Bkzs%2FMZX2KGo%3D

Open in new window


I know this sounds largely vague, but if you understand HTTP requests and TIdHTTP and then understand OAuth then making calls to the RESTful web service is a piece of cake.
Avatar of danz67

ASKER

I have find this article http://www.delphipraxis.net/161667-oauth.html
but when run the project and put parametre for access i get this error

what wrong?
Schermata-03-2456008-alle-22.01..png
That other post uses version 0 of the API. They are on version 1 now, things are a little different. Review the documentation of the latest version for reference. In most cases it simply means change the "0" from the URLs to a "1" but in some cases there are other differences in the URL. I would start your application using version 1 in case things change. The request_token, authorize, and access_token methods should be the same, but the 1 needs to be in the URL.

The base URLs look like...

https://api.dropbox.com/1/oauth/request_token
https://www.dropbox.com/1/oauth/authorize
https://api.dropbox.com/1/oauth/access_token

The sample code also does not use HTTPS. Not a huge deal, but if you are using TIdHTTP you can use HTTPS if you download OpenSSL library. First, get your stuff working and then Google TIdHTTP and SSL and set it up for SSL access.

That sample code looks good. It does things slightly different than how I did it, but it should still be OK. I setup my TOAuthRequest like this for example...

// When requesting the token...
  OAuthRequest := TOAuthRequest.Create('');
  HMAC := SignatureMethodClass.Create;
  try
    { Generate and sign the request }
    OAuthRequest.HTTPURL :=  'https://api.dropbox.com/1/oauth/request_token';    OAuthRequest.FromConsumerAndToken(FOAuthConsumer, nil, '');
    OAuthRequest.Sign_Request(HMAC, FOAuthConsumer, nil);

Open in new window


It's a minor difference and probably has no impact.

So, why are you getting 401? Do you have a an API token/secret pair from Dropbox? If not, you need to obtain one first. If you do then chances are good that your app is not yet published. This means you can only access your own account. That is, the account that owns the API token/secret pair you are using. Until your app is published this is how it is. If you are doing these things then trap the return message from Dropbox. Wrap your HTTP.Get calls in a try except.

try
  http.get(URL);
except
    { Log exceptions and re-raise }
    on E: EIdHTTPProtocolException do
    begin
      ShowMessage(E.Message);
      Raise;
    end;

Open in new window


E.Message above contains a message in JSON format that will be more helpful. Grab that and post it here. ***BUT*** That message WILL contain your super secret API token/secret pair. Anywhere in there that you see oauth_token=<something> or oauth_token_secret=<something> then replace <something> with * or similar to mask that before sharing. If you post it I'll look at it. That will help.
Avatar of danz67

ASKER

nothing to do, I always error. I hope you will give me a complete and ready example that works with Delphi 7.
Thanks for your time, hello
Check following links...

Units/Tools to communicate with Dropbox using Delphi...
DropboxDelphi / Dropbox.pas

How to upload files with the Dropbox REST API

Dropbox api for Delphi?
nothing to do, I always error.

I do not understand. My previous reply included code to retrieve the error message. Your 401 error is accompanied by a very useful error message that will describe your problem. Somewhere in your code you are using an HTTP component, presumably TIdHTTP, to send a request to Dropbox. Wrap all of your "get" calls in a try..except and handle the EIdHTTPProtocolException exception. E.ErrorMessage will contain the full error message and this will help. I mistated E.Message in my first reply, but E.ErrorMessage is what you are after.

For example, I have modified my application to use API key that does not yet have production status from Dropbox. I then tried to login to another user's account. In both error messages below an HTTP 401 error was raised, but look at the error message that was returned. It tells me exactly what the error condition was! Please do this and send the message. The post you sent earlier should work fine, but again, it's version 0 of the API. At some point they will discontinue support for this. Modify that code to use version 1 of the API. The URLs have the following substring... "/0/" in them. Change those to "/1/"

 {
        "error": "Access token not found."
    }

Open in new window


{
        "error": "Only a limited set of users can receive access tokens while this app is in development mode"
    }

Open in new window

Avatar of danz67

ASKER

"MichaelStaszewski"

forgive my inability to do what you asked me, I hope you can prove the project I have led Example DropBox Delphi and modify it to do it function properly, including file uploads, as I wrote in my initial request.
I know I ask too much but unfortunately I can not, hello and thanks
The sample app you sent should work for authentication, even if it does use version 0 of the API. I will confirm when I am in the office later. It is nearly identical to my project which works. If you are getting 401 errors during authentication the we must figure out why. I do not understand why you cannot trap the exception and review its message. This is standard debugging practice for any application.
ASKER CERTIFIED SOLUTION
Avatar of MichaelStaszewski
MichaelStaszewski
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of danz67

ASKER

FDropbox where is declared?
In your form somewhere. Just add it to the private section of your form.
I should have said, for testing put it in your form somewhere. In a real world application you're probably better off creating a singleton somewhere that is accessible to all parts of your application that may need it.
Avatar of danz67

ASKER

Let me explain what I did:

1. I created a new project
2. I imported the Dropbox Unit that I have provided
3. I imported OAuth
4. I put 2 buttons
5. I put the code as attached as I have explained

I can only understand this blessed FDropbox where and how it developed.
I think the last point and then we're done, to implement the various methods for uploading and downloading as I take the official documentation of DropBox, I used already in an iPhone app that I made the other day.
Excuse me for my inability, but I have little familiarity with these things, thanks
Unless I am mistaken this is a place to ask for assistance when you get stumped. If you would like to hire me to write the code for you we can work something out. Until then if you are out of your league with Delphi you should perhaps start running through some of the demos to understand the basics of it. I'd also learn key debugging skills. It does not appear that you are familiar with exception handling and how to debug the error messages so I do not feel that you are capable of developing and maintaining the application with your current knowledge. I am more than willing to help you if you get into a bind with this. But I cannot give you my complete code that my company owns nor will I write the complete Dropbox code for you for free. It is unfortunate, but you seem unwilling to try this for yourself.

I think the last point and then we're done, to implement the various methods for uploading and downloadin

I assume that you have tried those methods in the code I sent. What error did you get? If it was an EIdHTTPProtocolException I've already handled that for you and placed the error message in a file for you. Look at the code and you will see where the error is saved to file. Assuming that you get an EIdHTTPProtocolException in DownloadFile or UploadFile it will be logged and the error message will tell you what is wrong. Be careful posting that to someone though because it will contain your API key/secret pair. You should mask those out prior to showing anyone.
Avatar of danz67

ASKER

Maybe I explained incorrectly (Google translate).
I have a good knowledge of Delphi, if you go on my site www.drivergest.it see what I can do. I not very familiar with Delphi but with this type of approach. I understand that you can not write code for me for free, so if it's a question of money let me know if you're willing to do this work for me. Thanks for your valuable time.
Avatar of danz67

ASKER

I am committed and I was able to run the code you gave me, I was very tired and could not concentrate. After pressing the button1 it opens the browser with request for access to Dropbox (or better authenticate the application), now I upload the files and I think we should use the put option, but how do I find a list of files on a grid?

Thanks for everything, hello
I will be in office tomorrow and can help better with my pc up. I'm glad you were able to authenticate. In my experience that was the hardest part.
> how do I find a list of files on a grid

This may have been lost in translation. I am not sure what you mean here.
OAuth.pas is open source. I've tried unsuccessfully to contact the author with my changes, but it seems to have a few issues. To TOAuthRequest I've added a new property called Method.

    property Method: string read FMethod write FMethod; // TOAuthRequest assumes HTTP GET always. Sometimes you need other methods.

Open in new window


Also, TOAuthSignatureMethod_HMAC_SHA1does not order the parameters correctly. This is evident when using Dropbox. The parameters in the URL including the OAuth params must be in alphabetical order before signing. To accommodate this you must modify the TOAuthSignatureMethod_HMAC_SHA1class in OAuth.pas or create a subclass as I've done and write a new build_signature method. If you go the subclass route the code I previously sent should be modified to create an instance of your TOAuthSignatureMethod_HMAC_SHA1 descendant. Here is my build_signature code.

{:------------------------------------------------------------------------------
@summary The original class has 2 main bugs. First, it assumes that GET is the
         only HTTP method used and has no support for POST, DELETE, etc. Second,
         the original class would always construct the base string by placing
         URL parameters first followed by the OAuth parameters. Per section 9.1.1
         of the OAuth specifications (http://oauth.net/core/1.0/#anchor14) this
         is incorrect. The base string should be constructed such that all parameters
         including the OAuth ones are sorted in alphabetical order.
@------------------------------------------------------------------------------}
function TYourOAuthSignatureMethod_HMAC_SHA1.build_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer; Token: TOAuthToken): string;
  {:----------------------------------------------------------------------------
  @summary 
  @----------------------------------------------------------------------------}
  function Base64Encode(const Input: TIdBytes): string;
  begin
    Result := TIdEncoderMIME.EncodeBytes(Input);
  end;

  {:----------------------------------------------------------------------------
  @summary 
  @----------------------------------------------------------------------------}
  function EncryptHMACSha1(Input, AKey: string): TIdBytes;
  begin
    with TIdHMACSHA1.Create do
    try
      Key := ToBytes(AKey);
      Result := HashValue(ToBytes(Input));
    finally
      Free;
    end;
  end;
var
  Parm, consec, toksec: string;
  SignableParams, Fields: TStringList;
  I: Integer;
begin
//  inherited; // <------ NO!

  Fields := TStringList.Create;
  SignableParams := TStringList.Create;
  try
    { Request.Fields is a string. We need the fields broken out into a list. }
    Fields.Delimiter := '&';
    Fields.DelimitedText := Request.Fields;

    { Params must be sorted to be OAuth compliant. See section 9.1.1 here: http://oauth.net/core/1.0/#anchor14 }
    SignableParams.Sorted := True;

    { Add all URL parameters }
    for I := 0 to Fields.Count - 1 do
      SignableParams.Add(Fields[I]);

    { Add all OAuth parameters, excluding oauth_signature }
    for I := 0 to Request.Parameters.Count - 1 do
      if Request.Parameters[I] <> 'oauth_signature' then
        SignableParams.Add(Request.Parameters[I]);

    { Build out the base string parameter }
    Parm := TOAuthUtil.urlEncodeRFC3986(Request.Scheme) +
      TOAuthUtil.urlEncodeRFC3986(Request.Host) +
      TOAuthUtil.urlEncodeRFC3986(Request.Path);

    for I := 0 to SignableParams.Count - 1 do
    begin
      if I = 0 then
        Parm := Parm + '&'
      else
        Parm := Parm + TOAuthUtil.URLEncodeRFC3986('&');

      Parm := Parm + TOAuthUtil.URLEncodeRFC3986(SignableParams[I]);
    end;

    { Set the base string }
    Request.BaseString := IfThen(Request.Method = '', 'GET', Request.Method) + '&' + Parm;

    { Generate the signature }
    if Token <> nil then
    begin
      consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
      toksec := TOAuthUtil.urlEncodeRFC3986(Token.Secret);
      consec := consec + '&' + toksec;
      Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec))
    end
    else
    begin
      consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
      consec := consec + '&';
      Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec));
    end;
  finally
    SignableParams.Free;
    Fields.Free;
  end;
end;

Open in new window


I am not sure what implications, if any, this will have on other projects using OAuth for other web services, but for Dropbox it works well.
Avatar of danz67

ASKER

> This may have been lost in translation. I am not sure what you mean here.

Let me explain. I am interested in viewing file dropbox, directly into a grid
You would like a listing of all files that are stored in Dropbox and you would like to display them in a grid. Is this correct? Is so, use the /metadata Dropbox API method. It returns metadata for a file or folder. You can iterate the data populating any control that you want.

Read the documentation on Dropbox's site. It covers all of these things well for you. Using the class I sent you it is a fairly simple task to make the calls into the REST API and process the results. In an earlier reply I mentioned that I use SuperObject to process the JSON. You will need to process the JSON for /metadata. For upload and download files you can probably get away without it, but for /metadata you will need to process the results. Delphi 7 does not have a JSON parser so SuperObject is your best bet. Google SuperObject and you will find it. There are others, but this is the one I use and have found it to be adequate for the JSON returned by Dropbox.

Best of luck to you, but I am going to abandon this discussion. I do not see you attempting anything on your own and coming back with specific questions and full descriptions of your problems. I see only questions that look like someone is asking others for working code to do what they want. You are the developer and should be writing your code!

Keep the OAuth post above around as you might need that. ;-) It's an open source project after all so this is my contribution.
Avatar of danz67

ASKER

Everything you say is right, and accept all of your suggestions that I have been very useful. I posted this last question just because you asked me before.

Now it's up to me ... Hello and thank you all;)