cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Announcements
We are making some updates so the Community might be down for a few hours on Monday the 11th of November. Apologies for the inconvenience and thank you for your patience. You can find out more here.

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.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Re: how can a script or app get a sharable dropbox link?

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

David S.118
Helpful | Level 5

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?

3 Replies 3

Здравко
Legendary | Level 20

Hi @David S.118,

You can take a look on thread' post here for an idea how that can be done. 😉

Hope this helps.

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.

Здравко
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:

изображение.png

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.

Need more support?