We're making changes to the Community, so you may have received some notifications - thanks for your patience and welcome back. Learn more here.

Forum Discussion

David S.118's avatar
David S.118
Helpful | Level 5
2 years ago

How can a script or app get a shareable Dropbox link?

I'm trying to automate a task I've been doing for a while that's taking way too much time, and it requires me to send a sharable link to someone for some files in my Dropbox. So I have a script I've written but ... where I'd normally right-click to get a sharable link for a file, how can I get such a link within the script? I know the file locally on my machine, but how can I get a sharable Dropbox link? Can I use the Dropbox API for that? or is there a simpler way?

    • David S.118's avatar
      David S.118
      Helpful | Level 5

      Thanks. I'm not sure what language that example script is in. 

       

      I need this to run in Windows.

       

      After looking through a bunch of related posts, it looks like there might be a command-line tool that I can run in Windows that does this.

      • Здравко's avatar
        Здравко
        Legendary | Level 20

        David S.118 wrote:

        ... I'm not sure what language that example script is in. 

        ...


        Hi again David S.118,

        Hah..😯 You said "I have a script I've written but...", but seems you don't have too much experience, since can distinguish one of the most popular scripting languages - Python. Some parts of the official Dropbox application is implemented in the same language also. Even more, it's mentioned in the script header line. Didn't you read it? Anyway... it's my wrong assumption; I'll try to be more descriptive.

         


        David S.118 wrote:

        ...

        I need this to run in Windows.

        ...


        Oh... The script, I linked to, targets Mac and Linux only; on Windows can run but not in the same form. I just made some tweaks to let it work on Windows. Since it's Python script, you have to have Python interpreter installed, if you haven't yet, to be able run it. So first download and install the interpreter from here. Make sure you have selected all accessibility on install time:

        Once the interpreter installed you may need some additional packages available, so make sure they are there. Execute following commands in terminal window:

        py -m pip install --upgrade pip
        py -m pip install platformdirs
        py -m pip install dropbox

        Now you have everything needed to run the script that let you get Dropbox links. The following is updated script version, working on Windows too:

        #!/usr/bin/python3
        ###############################################################################
        #    Script receiving Dropbox links based on referred local files/folders
        #    --------------------------------------------------------------------
        # Every target file/folder is passed as argument to the script. Resulted links
        # are passed in the same sequence one per line to standard output (incorrect
        # are skipped).
        # Two preparation steps are needed:
        #   1. Register a Dropbox application and put the application key as value of
        #      APPLICATION_KEY global variable.
        #   2. Register the used REDIRECT_URI in the application just created in
        #      previous step. With host 'localhost' and port 8080, the redirect URL
        #      that has to be registered is "http://localhost:8080/", for instance.
        # Next, just make it executable (if needed), using:
        #   $ chmod a+x get_dropbox_link
        # ... and put it in a place visible for execution in your system (somewhere in
        # folders pointed by $PATH environment variable). On first run you will be
        # invited to link your script to your Dropbox account. To work correct local
        # Dropbox application and this script have to be link to the same account!
        # Verified on Dropbox v172.4.7555
        # Author: Здравко
        #   www.dropboxforum.com/t5/user/viewprofilepage/user-id/422790
        ###############################################################################
        
        from dropbox import Dropbox, DropboxOAuth2Flow
        from dropbox.exceptions import ApiError
        from dropbox.oauth import NotApprovedException
        import json
        from pathlib import Path
        from datetime import datetime
        from os import sep, name, makedirs
        from sys import exit
        import logging
        from platformdirs import user_config_path
        
        # Place to save current configuration
        CONFIG_JSON=user_config_path('get_dropbox_link') / 'cred.json'
        
        # Take a look on your application in https://www.dropbox.com/developers/apps
        APPLICATION_KEY='PUT YOUR KEY HERE'
        
        URI_HOST='localhost'
        URI_PORT=8080
        # URI should be registered in the application redirect URIs list!!!
        REDIRECT_URI=f"http://{URI_HOST}:{URI_PORT}/"
        
        success_response = "End of authentication flow. 😉 Your can get a link!"
        cancel_response = "🤷 You have denied your application's work. 😕"
        error_response = "😈 You got an error: "
        
        class ApplicationConfig:
          def __init__(self, conf_path=CONFIG_JSON):
            self.conf_path=Path(conf_path).expanduser()
            self.conf=None
            self.client=None
            self.access_token=None
            self.access_token_expiresat=None
            self.refresh_token=None
            makedirs(self.conf_path.parent, exist_ok=True)
            if self.conf_path.is_file():
              try:
                with self.conf_path.open() as fconf:
                  self.conf=json.load(fconf)
                self.access_token = self.conf['access_token']
                self.access_token_expiresat = datetime.fromtimestamp(
                  self.conf['access_token_expiresat'])
                self.refresh_token = self.conf['refresh_token']
              except Exception:
                self.conf_path.unlink(True)
                self.conf=None
          def __del__(self):
            "Checks for something changed (new access token) and dumps it when there is"
            if (self.client is not None and
                self.client._oauth2_access_token_expiration >
                self.access_token_expiresat):
              self.conf['access_token'] = self.client._oauth2_access_token
              self.conf['access_token_expiresat'] = (
                self.client._oauth2_access_token_expiration.timestamp())
              self.conf['refresh_token'] = self.client._oauth2_refresh_token
              with self.conf_path.open(mode='w') as fconf:
                json.dump(self.conf, fconf)
          def getClient(self):
            "Gets Dropbox client object. Performs OAuth flow if needed."
            if self.conf is None:
              self.client=None
              import webbrowser
              from http.server import HTTPServer, BaseHTTPRequestHandler
              dbxAuth=DropboxOAuth2Flow(APPLICATION_KEY, REDIRECT_URI, {},
                'dropbox-auth-csrf-token', token_access_type='offline', use_pkce=True)
              webbrowser.open(dbxAuth.start())
              conf=None
              conf_path = self.conf_path
              class Handler(BaseHTTPRequestHandler):
                response_success = success_response.encode()
                response_cancel = cancel_response.encode()
                response_error = error_response.encode()
                def do_GET(self):
                  nonlocal dbxAuth, conf
                  from urllib.parse import urlparse, parse_qs
                  query = parse_qs(urlparse(self.path).query)
                  for r in query.keys():
                    query[r] = query[r][0]
                  self.send_response(200)
                  self.send_header("content-type", "text/plain;charset=UTF-8")
                  try:
                    oauthRes = dbxAuth.finish(query)
                    conf={'access_token': oauthRes.access_token,
                          'access_token_expiresat': oauthRes.expires_at.timestamp(),
                          'refresh_token': oauthRes.refresh_token}
                    with conf_path.open(mode='w') as fconf:
                      json.dump(conf, fconf)
                  except NotApprovedException:
                    conf={}
                    self.send_header("content-length", f"{len(Handler.response_cancel)}")
                    self.end_headers()
                    self.wfile.write(Handler.response_cancel)
                    self.wfile.flush()
                    return
                  except Exception as e:
                    conf={}
                    r = Handler.response_error + str(e).encode()
                    self.send_header("content-length", f"{len(r)}")
                    self.end_headers()
                    self.wfile.write(r)
                    self.wfile.flush()
                    return
                  self.send_header("content-length", f"{len(Handler.response_success)}")
                  self.end_headers()
                  self.wfile.write(Handler.response_success)
                  self.wfile.flush()
              httpd=HTTPServer((URI_HOST, URI_PORT), Handler)
              while conf is None:
                httpd.handle_request()
              httpd.server_close()
              del httpd
              if 'refresh_token' not in conf:
                raise RuntimeError("Cannot process because missing authentication")
              self.conf = conf
              self.access_token = self.conf['access_token']
              self.access_token_expiresat = datetime.fromtimestamp(
                self.conf['access_token_expiresat'])
              self.refresh_token = self.conf['refresh_token']
            # Makes sure there is cached client object.
            if self.client is None:
              self.client=Dropbox(self.access_token,
                            oauth2_refresh_token=self.refresh_token,
                            oauth2_access_token_expiration=self.access_token_expiresat,
                            app_key=APPLICATION_KEY)
            return self.client
        
        class PathMapper:
          def __init__(self):
            if name == 'nt':
              dbx_info = user_config_path() / 'Dropbox' / 'info.json'
            else:
              dbx_info = Path('~/.dropbox/info.json').expanduser()
            if not dbx_info.is_file():
              raise RuntimeError("Missing Dropbox application information")
            with dbx_info.open() as finfo:
              # Only personal accounts are supported by now - group accounts need
              # additional namespace handling (just changing 'personal' is not enough).
              # Somebody else may make some exercises. 
              self.dbx_path = json.load(finfo)['personal']['path']
          def __contains__(self, path):
            path = str(Path(path).expanduser().absolute())
            return ((len(path) == len(self.dbx_path) and path == self.dbx_path) or
                    (len(path) > len(self.dbx_path) and path[len(self.dbx_path)] == sep
                     and path[:len(self.dbx_path)] == self.dbx_path))
          def __getitem__(self, path):
            path = str(Path(path).expanduser().absolute())
            if ((len(path) == len(self.dbx_path) and path == self.dbx_path) or
                (len(path) > len(self.dbx_path) and path[len(self.dbx_path)] == sep
                 and path[:len(self.dbx_path)] == self.dbx_path)):
              return Path(path[len(self.dbx_path):]).as_posix()
        
        def main():
          import argparse
          dbxPathMap = PathMapper()
          parser = argparse.ArgumentParser(description="Fetch Dropbox URL for path")
          parser.add_argument("paths", type=str, nargs="+", help="paths to files")
          parser.add_argument("--verbose", "-v", action="store_true",
                              help="toggle verbose mode")
          args = parser.parse_args()
          del parser
          
          if args.verbose:
            logging.basicConfig(level=logging.DEBUG)
          
          conf = ApplicationConfig()
          dbx = conf.getClient()
          
          for path in args.paths:
            if path not in dbxPathMap:
              logging.error(f"Passed path '{path}' is not part of the Dropbox driectory tree")
              continue
            dbx_path = dbxPathMap[path]
            if len(dbx_path) == 0:
              logging.error("Dropbox folder itself cannot be pointed by link")
              continue
            logging.debug(f"Processing local file '{path}' with Dropbox path '{dbx_path}'")
            try:
              metadata = dbx.sharing_create_shared_link_with_settings(dbx_path)
            except ApiError as e:
              er = e.error
              if not er.is_shared_link_already_exists():
                raise
              er = er.get_shared_link_already_exists()
              if not er.is_metadata():
                raise
              metadata = er.get_metadata()
            print(metadata.url)
        
        if __name__ == "__main__":
          try:
            main()
          except Exception as e:
            logging.error(f"Unexpected error: {e}")
            exit(1)

        Save above script in a file named "get_dropbox_link" on place convenient for you. Since this script uses Dropbox API, you need to register your application here. Select "Full Dropbox" as access type, since you will need access to files in entire account, not only application specific files. Make sure "sharing.write" is selected scope in your application permission (to be able create and get shared links). In section Redirect URIs, ensure URL "http://localhost:8080/" is present (add it as needed). On the same page is field named App key; copy the value/key shown there to corresponding place in the script (near beginning - it's the only redaction you have to make). Now you're almost ready. Type in the terminal something like:

        py <exec path to>\get_dropbox_link <path into Dropbox>

        Here <exec path to> is optional path to the place you put the script on. You may need to use such explicit path when the script is not directly visible from the place you're calling from. <path into Dropbox> is path to file/folder, residing within your local Dropbox folder, you want to get shared link to. That's it. 😉 You can ether extend this script and/or call it from any other script (doesn't have to be Python script; can be any other script).

        Good luck.

About Create, upload, and share

Find help to solve issues with creating, uploading, and sharing files and folders in Dropbox. Get support and advice from the Dropbox Community.

Need more support

If you need more help you can view your support options (expected response time for an email or ticket is 24 hours), or contact us on X or Facebook.

For more info on available support options for your Dropbox plan, see this article.

If you found the answer to your question in this Community thread, please 'like' the post to say thanks and to let us know it was useful!