Working with Files
Upload, download, copy, move, delete, share, and manage files in SharePoint
document libraries — the everyday operations for building on top of documents.
Files live inside document libraries, organized in folders. Separately,
list items can have attachments — see
listitems/attachments/ for those.
graph TD
subgraph Site
Library["Document Library e.g. Shared Documents"]
end
subgraph Library
Folder["Folder"]
File["File"]
Folder --> File
end
Library --> Folder
Library --> File
Authentication
SharePoint's /_api app-only flow does not accept a client secret — use a
delegated sign-in (username & password, no MFA) or a client certificate for
app-only automation. The examples here use username & password:
from office365.sharepoint.client_context import ClientContext
ctx = ClientContext("https://contoso.sharepoint.com/sites/team").with_username_and_password(
tenant="contoso.onmicrosoft.com", client_id="client_id", username="user@contoso.com", password="password"
)
with_client_secret(...) is a common mistake for SharePoint. It works
for Microsoft Graph, but not for ClientContext — use
with_username_and_password (below) or with_client_certificate(...)
(app-only). See auth/ for the full matrix.
Quick start
Upload a small file, then download it back:
# Upload a file (< 4 MB)
with open("./report.docx", "rb") as f:
uploaded = ctx.web.default_document_library().root_folder.upload_file("report.docx", f.read()).execute_query()
print(f"Uploaded: {uploaded.server_relative_url}")
# Download it back
downloaded = uploaded.get_content().execute_query()
print(f"Downloaded: {len(downloaded.content)} bytes")
Upload
| What |
File |
Notes |
| Upload a small file |
upload.py |
File < 4 MB |
| Upload a large file |
upload_large.py |
Chunked upload session |
| Upload with a progress bar |
upload_with_progress.py |
tqdm progress for large files |
| Upload a path or stream |
FileCollection.upload_file |
Simple request or resumable session, picked by size |
| Ensure a file exists |
Folder.ensure_file |
Deferred get-or-upload; re-runs are no-ops |
| Upload with checksum |
upload_with_checksum.py |
MD5 verification |
| Upload CSV data |
upload_csv.py |
Data files |
| Upload JSON data |
upload_json.py |
Data files |
| Replace content |
replace.py |
Overwrite via binary stream |
FileCollection.upload_file takes a local path or an open stream and picks the
transport for you (single request at or below chunk_size, a resumable session
above it). Folder.ensure_file is the idempotent counterpart of
Folder.upload_file: it creates missing parent folders, then reuses the file if
it already exists (on_conflict="skip", the default) or overwrites it
(on_conflict="replace").
# One call, sized automatically — queues the right upload for the file's size
file = target_folder.files.upload_file("./archive/big.mp4").execute_query()
# Get-or-upload: parent folders are created; a re-run is a no-op
file = target_folder.ensure_file("2026/Q1/report.txt", "hello").execute_query()
Download
# Download to a local file
with open("report.docx", "wb") as f:
ctx.web.get_file_by_server_relative_path("Shared Documents/report.docx").download(f).execute_query()
Bulk operations
The highest-leverage scripts for migrations and backups:
# Bulk upload: queue, then flush in one batch request
for name in os.listdir("./data"):
with open(f"./data/{name}", "rb") as f:
target_folder.upload_file(name, f.read()) # queue
ctx.execute_batch() # one request per batch
# Backup a folder with its full version history
with open("archive.zip", "wb") as f:
folder.download_folder(f, include_versions=True).execute_query()
Copy & Move
Delete
| What |
File |
Notes |
| Delete / recycle |
delete.py |
Permanent or recycle bin |
Check Out & Approvals
For libraries with required check-out or content approval:
Locked files (open in Office)
A file open in Office holds a shared lock — different from a check-out
lock. An overwrite fails with SPFileLockedException (HTTP 423), because the
API cannot break a shared lock. The library makes the failure catchable and
offers opt-in remedies:
| What |
File |
Notes |
| Retry / metadata / bypass delete |
handle_locked_file.py |
retry_on(FileLockedException), update_ex(bypass_shared_lock=True), delete_object(bypass_shared_lock=True) |
Acting on behalf of a user
SharePoint REST has no "impersonate" switch — a request runs as whoever the
access token represents, so a check-out is always attributed to the
authenticated identity:
- As a specific user — sign in with a delegated flow for that user
(
with_username_and_password, with_interactive, with_device_flow, or a
delegated token supplied via with_access_token, e.g. an on-behalf-of token).
checkout_checkin.py uses this flow.
- As the application — app-only credentials (
with_client_certificate) run
as the app, not a human user, so they cannot be used to attribute a check-out
to someone else.
To release a file another user left checked out, list it with
get_checked_out.py and take over the check-out
(CheckedOutFile.takeover_checkout()), which requires sufficient permissions.
Sharing
Create Documents
Permissions
Versions
DataFrames
Read/write a file's content as a pandas DataFrame (CSV/XLSX/JSON/Parquet);
file metadata stays as plain properties (file.name, file.length, ...).
| What |
File |
Notes |
| Write a DataFrame to a library file |
write_dataframe.py |
CSV (UTF-8 BOM) or XLSX; symmetric read via Folder.read_dataframe / File.read_dataframe |
Audit & Compliance
Attachments
Attachments are files attached to list items, not documents in a library —
see listitems/attachments/ for upload, download,
list, and delete operations.
API reference