Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Delphi - DropBox

Posted on 2012-03-21
22
Medium Priority
?
4,239 Views
Last Modified: 2012-06-27
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
0
Comment
Question by:danz67
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 12
  • 9
22 Comments
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37749540
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.
0
 

Author Comment

by:danz67
ID: 37749931
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
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37750166
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.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 

Author Comment

by:danz67
ID: 37751240
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
0
 
LVL 19

Expert Comment

by:Thommy
ID: 37751358
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?
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37752490
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

0
 

Author Comment

by:danz67
ID: 37756203
"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
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37756771
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.
0
 
LVL 4

Accepted Solution

by:
MichaelStaszewski earned 2000 total points
ID: 37757658
I am on XE2 so I do not know if this will compile under D7, but try the following code. Use the attached unit in your project. Add it to your form's uses list and add two buttons and implement the OnCreate and OnDestroy handlers. Set the code in there as follows. Does your app have application folder access only or full access to Dropbox? If full access to Dropbox then FDropbox.Root below should be set to 'dropbox' otherwise if your app has application folder access only then leave it as is.

There are particular challenges here that you will need to overcome. I have not provided everything for you. I would like to see you show some initiative in learning how to do this. I have; however, provided a class that will authorize properly using the latest version of the API. It also has skeleton functions in there for uploading and downloading, but you will encounter problems in each. The download file routine should fail for you. If I recall correctly OAuth.pas returns an invalid signature string after authentication. (Hint) I think it has extra items in it. I have wrapped the HTTP call in try except and even write out the exception to your C:\ drive for you to look at. It should tell you exactly what the problem is. Upload file should fail for the same reason as DownloadFile, but it also uses the HTTP PUT method. OAuth.pas assumes that all calls are GET and thus signs non-GET requests incorrectly. You need to modify OAuth.pas.

I will be glad to help you with any questions you have, but you must try to understand what it is you are doing because if your application is something you want to share with others you will run into more issues with proxy servers and the like that are more complicated than simply putting a file on Dropbox.

The other sample code you have found is poorly written, uses the old API, and does not use HTTPS. This code uses HTTPS and in order for it to work you must download OpenSSL. Google it, it's an open source project. There are 2 DLLs that you need to put into the same folder as your executable.

procedure TForm1.Button1Click(Sender: TObject);
begin
  FDropbox.AppKey := YourAppKey;
  FDropbox.AppSecret := YourAppSecret;
  FDropbox.GetRequestToken;
  FDropbox.Authorize;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  FDropbox.GetAccessToken;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDropbox := TDropbox.Create;
  FDropbox.Root := 'sandbox';
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FDropbox.Free;
end;

Open in new window

Dropbox.zip
0
 

Author Comment

by:danz67
ID: 37758489
FDropbox where is declared?
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758498
In your form somewhere. Just add it to the private section of your form.
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758502
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.
0
 

Author Comment

by:danz67
ID: 37758765
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
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37758835
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.
0
 

Author Comment

by:danz67
ID: 37758882
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.
0
 

Author Comment

by:danz67
ID: 37763189
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
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37764042
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.
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767487
> 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.
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767513
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.
0
 

Author Comment

by:danz67
ID: 37767742
> 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
0
 
LVL 4

Expert Comment

by:MichaelStaszewski
ID: 37767890
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.
0
 

Author Comment

by:danz67
ID: 37767963
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;)
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
We’ve all felt that sense of false security before—locking down external access to a database or component and feeling like we’ve done all we need to do to secure company data. But that feeling is fleeting. Attacks these days can happen in many w…
Please read the paragraph below before following the instructions in the video — there are important caveats in the paragraph that I did not mention in the video. If your PaperPort 12 or PaperPort 14 is failing to start, or crashing, or hanging, …
Suggested Courses

609 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question