Some googledrive functions are built to naturally handle multiple files, while others operate on a single file.
Functions that expect a single file:
-
drive_browse()
-
drive_cp()
drive_download()
drive_ls()
-
drive_mv()
drive_put()
-
drive_rename()
drive_update()
drive_upload()
Functions that allow multiple files:
In general, the principle is: if there are multiple parameters that
are likely to vary across multiple files, the function is designed to
take a single input. In order to use these function with multiple
inputs, use them together with your favorite approach for iteration in
R. Below is a worked example, focusing on tools in the tidyverse, namely
the map()
functions in purrr.
Upload multiple files, then rename them
Scenario: we have multiple local files we want to upload into a folder on Drive. Then we regret their original names and want to rename them.
Load packages.
Upload
Use the example files that ship with googledrive.
local_files <- drive_examples_local()
local_files <- set_names(local_files, basename(local_files))
local_files
#> chicken.csv
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/chicken.csv"
#> chicken.jpg
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/chicken.jpg"
#> chicken.pdf
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/chicken.pdf"
#> chicken.txt
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/chicken.txt"
#> imdb_latin1.csv
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/imdb_latin1.csv"
#> markdown.md
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/markdown.md"
#> r_about.html
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/r_about.html"
#> r_logo.jpg
#> "/home/runner/work/_temp/Library/googledrive/extdata/example_files/r_logo.jpg"
Create a folder on your Drive and upload all files into this folder
by iterating over the local_files
using
purrr::map()
.
folder <- drive_mkdir("upload-into-me-article-demo")
#> Created Drive file:
#> • upload-into-me-article-demo <id: 1z7EqvLCDsrySxwGZ6wMQaNqiwxwt_n0n>
#> With MIME type:
#> • application/vnd.google-apps.folder
with_drive_quiet(
files <- map(local_files, ~ drive_upload(.x, path = folder))
)
First, let’s confirm that we uploaded the files into the new folder.
drive_ls(folder)
#> # A dribble: 8 × 3
#> name id drive_resource
#> <chr> <drv_id> <list>
#> 1 r_logo.jpg 1TV7xqkQUhA-f0sUphxNLlz02cOtPCM6e <named list [45]>
#> 2 r_about.html 1ZPPxwk5d_5UukMu9oaBL0cL2np9lBZxf <named list [44]>
#> 3 markdown.md 1sLFBGg3ixkZ5KSjOU3pNXE5LXX_QO_gv <named list [43]>
#> 4 imdb_latin1.csv 1r4DM0JWkfCLvoQUEKrbeiLrcZ_EYTn3e <named list [43]>
#> 5 chicken.txt 1nPeORE0Fv2YcqydApkV4xE14zk-_r_p4 <named list [44]>
#> 6 chicken.pdf 1MaOJ1j_HZMBP5PsmVxkcNGIgl2kSpCj4 <named list [44]>
#> 7 chicken.jpg 1Vhv6BzLcJCh_8Ox2VsPDz3SlJnS8RMBA <named list [45]>
#> 8 chicken.csv 1_EU7Skd6GbHhLohiTqEyhdpvByvjTEXw <named list [44]>
Now let’s reflect on the files
object returned by this
operation. files
is a list of dribbles,
one per uploaded file.
str(files, max.level = 1)
#> List of 8
#> $ chicken.csv : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ chicken.jpg : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ chicken.pdf : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ chicken.txt : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ imdb_latin1.csv: dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ markdown.md : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ r_about.html : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
#> $ r_logo.jpg : dribble [1 × 3] (S3: dribble/tbl_df/tbl/data.frame)
This would be a favorable data structure if you’ve got more
map()
ing to do, as you’ll see below.
But what if not? You can always row bind individual dribbles into one
big dribble yourself with, e.g., dplyr::bind_rows()
.
bind_rows(files)
#> # A dribble: 8 × 3
#> name id drive_resource
#> <chr> <drv_id> <list>
#> 1 chicken.csv 1_EU7Skd6GbHhLohiTqEyhdpvByvjTEXw <named list [43]>
#> 2 chicken.jpg 1Vhv6BzLcJCh_8Ox2VsPDz3SlJnS8RMBA <named list [45]>
#> 3 chicken.pdf 1MaOJ1j_HZMBP5PsmVxkcNGIgl2kSpCj4 <named list [43]>
#> 4 chicken.txt 1nPeORE0Fv2YcqydApkV4xE14zk-_r_p4 <named list [43]>
#> 5 imdb_latin1.csv 1r4DM0JWkfCLvoQUEKrbeiLrcZ_EYTn3e <named list [43]>
#> 6 markdown.md 1sLFBGg3ixkZ5KSjOU3pNXE5LXX_QO_gv <named list [43]>
#> 7 r_about.html 1ZPPxwk5d_5UukMu9oaBL0cL2np9lBZxf <named list [43]>
#> 8 r_logo.jpg 1TV7xqkQUhA-f0sUphxNLlz02cOtPCM6e <named list [45]>
Below we show another way to finesse this by using a variant of
purrr::map()
that does this for us, namely
map_dfr()
.
Rename
Imagine that we now wish these file names had a date prefix. First,
form the new names. We use glue::glue()
for string
interpolation but you could also use paste()
. Second, we
map over two inputs: the list of dribbles from above and the vector of
new names.
(new_names <- glue("{Sys.Date()}_{basename(local_files)}"))
#> 2025-09-10_chicken.csv
#> 2025-09-10_chicken.jpg
#> 2025-09-10_chicken.pdf
#> 2025-09-10_chicken.txt
#> 2025-09-10_imdb_latin1.csv
#> 2025-09-10_markdown.md
#> 2025-09-10_r_about.html
#> 2025-09-10_r_logo.jpg
files_dribble <- map2_dfr(files, new_names, drive_rename)
#> Original file:
#> • chicken.csv <id: 1_EU7Skd6GbHhLohiTqEyhdpvByvjTEXw>
#> Has been renamed:
#> • 2025-09-10_chicken.csv <id: 1_EU7Skd6GbHhLohiTqEyhdpvByvjTEXw>
#> Original file:
#> • chicken.jpg <id: 1Vhv6BzLcJCh_8Ox2VsPDz3SlJnS8RMBA>
#> Has been renamed:
#> • 2025-09-10_chicken.jpg <id: 1Vhv6BzLcJCh_8Ox2VsPDz3SlJnS8RMBA>
#> Original file:
#> • chicken.pdf <id: 1MaOJ1j_HZMBP5PsmVxkcNGIgl2kSpCj4>
#> Has been renamed:
#> • 2025-09-10_chicken.pdf <id: 1MaOJ1j_HZMBP5PsmVxkcNGIgl2kSpCj4>
#> Original file:
#> • chicken.txt <id: 1nPeORE0Fv2YcqydApkV4xE14zk-_r_p4>
#> Has been renamed:
#> • 2025-09-10_chicken.txt <id: 1nPeORE0Fv2YcqydApkV4xE14zk-_r_p4>
#> Original file:
#> • imdb_latin1.csv <id: 1r4DM0JWkfCLvoQUEKrbeiLrcZ_EYTn3e>
#> Has been renamed:
#> • 2025-09-10_imdb_latin1.csv <id: 1r4DM0JWkfCLvoQUEKrbeiLrcZ_EYTn3e>
#> Original file:
#> • markdown.md <id: 1sLFBGg3ixkZ5KSjOU3pNXE5LXX_QO_gv>
#> Has been renamed:
#> • 2025-09-10_markdown.md <id: 1sLFBGg3ixkZ5KSjOU3pNXE5LXX_QO_gv>
#> Original file:
#> • r_about.html <id: 1ZPPxwk5d_5UukMu9oaBL0cL2np9lBZxf>
#> Has been renamed:
#> • 2025-09-10_r_about.html <id: 1ZPPxwk5d_5UukMu9oaBL0cL2np9lBZxf>
#> Original file:
#> • r_logo.jpg <id: 1TV7xqkQUhA-f0sUphxNLlz02cOtPCM6e>
#> Has been renamed:
#> • 2025-09-10_r_logo.jpg <id: 1TV7xqkQUhA-f0sUphxNLlz02cOtPCM6e>
We use purrr::map2_dfr()
to work through
files
, the list of dribbles (= Drive files), and
new_names
, the vector of new names, and row bind the
returned dribbles into a single dribble holding all files.
Let’s check on the contents of this folder again to confirm the new names:
drive_ls(folder)
#> # A dribble: 8 × 3
#> name id drive_resource
#> <chr> <drv_id> <list>
#> 1 2025-09-10_r_logo.jpg 1TV7xqk… <named list [45]>
#> 2 2025-09-10_r_about.html 1ZPPxwk… <named list [44]>
#> 3 2025-09-10_markdown.md 1sLFBGg… <named list [43]>
#> 4 2025-09-10_imdb_latin1.csv 1r4DM0J… <named list [43]>
#> 5 2025-09-10_chicken.txt 1nPeORE… <named list [44]>
#> 6 2025-09-10_chicken.pdf 1MaOJ1j… <named list [44]>
#> 7 2025-09-10_chicken.jpg 1Vhv6Bz… <named list [45]>
#> 8 2025-09-10_chicken.csv 1_EU7Sk… <named list [44]>
Let’s confirm that, by using map2_df2()
instead of
map2()
, we got a single dribble back, instead of a list of
one-row dribbles:
files_dribble
#> # A dribble: 8 × 3
#> name id drive_resource
#> <chr> <drv_id> <list>
#> 1 2025-09-10_chicken.csv 1_EU7Sk… <named list [44]>
#> 2 2025-09-10_chicken.jpg 1Vhv6Bz… <named list [45]>
#> 3 2025-09-10_chicken.pdf 1MaOJ1j… <named list [44]>
#> 4 2025-09-10_chicken.txt 1nPeORE… <named list [44]>
#> 5 2025-09-10_imdb_latin1.csv 1r4DM0J… <named list [43]>
#> 6 2025-09-10_markdown.md 1sLFBGg… <named list [43]>
#> 7 2025-09-10_r_about.html 1ZPPxwk… <named list [44]>
#> 8 2025-09-10_r_logo.jpg 1TV7xqk… <named list [45]>
What if you wanted to get a list back, because your downstream
operations include yet more map()
ing? Then you would use
map2()
.
files_list <- map2(files, new_names, drive_rename)
Clean up
Our trashing function, drive_trash()
is vectorized and
can therefore operate on a multi-file dribble. We could trash these
files like so:
drive_trash(files_dribble)
If you’re absolutely sure of yourself and happy to do something
irreversible, you could truly delete these files with
drive_rm()
, which is also vectorized:
drive_rm(files_dribble)
Finally – and this is the code we will actually execute – the easiest way to delete these files is to delete their enclosing folder.
drive_rm(folder)
#> File deleted:
#> • upload-into-me-article-demo <id: 1z7EqvLCDsrySxwGZ6wMQaNqiwxwt_n0n>