Your workflow is unique 👨💻 - tell us how you use Dropbox here.
Forum Discussion
Omri1984
1 month agoExplorer | Level 4
Using API and SDK together
Hello,
I need some help
until now i have been using >NET SDK Version 6.37.
and we are experiencing a problem downloading large files 30GB and higher.
so I tried to implement chunk download, but SDK does not support it .
so I did it using the API.
but my problem is that when we use the SDK we do not Need to refresh token.
and when we use api after some time I am getting 401 .
so I implemented a refresh token method, and refreshed the token , but still after sending the request again with the new token I got I am still getting the 401.
I tried to get the token from the Dropbox Client but it is not there
also regarding the refresh token , is this token always stays the same ?
can some one help?
adding some code snipped.
public async Task<string> RefreshAccessTokenAsync(string expiredAccessToken,BaseRequest baserRequest,bool isBusinessClient, CancellationToken token)
{
using (var http = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.dropboxapi.com/oauth2/token");
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "refresh_token" },
{ "refresh_token", baserRequest.RefreshToken },
{ "client_id", isBusinessClient ?ClientIdBusiness : ClientId },
{ "client_secret", isBusinessClient ? ClientSecretBusiness : ClientSecret }
});
request.Content = content;
using (var response = await http.SendAsync(request, token))
{
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
string newAccessToken = result.access_token;
if (string.IsNullOrEmpty(newAccessToken))
throw new Exception("Dropbox token refresh failed: access_token not returned.");
return newAccessToken;
}
}
}
private async Task DownloadFileInChunksHttpAsync(
string accessToken,
string dropboxPath,
string localPath,
long fileSize,
string asMember,
string rootNamespaceId,
CancellationToken token,
BaseRequest baseRequest,
bool isBusinessClient) // <-- tells refresh which client to use
{
DropboxTeamClient client;
const int bufferSize = 1024 * 1024; // 1MB
const long chunkSize = 50L * 1024 * 1024; // 50MB
using (var fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.None))
using (var http = new HttpClient())
{
string currentAccessToken = accessToken;
long offset = 0;
while (offset < fileSize)
{
long end = Math.Min(offset + chunkSize - 1, fileSize - 1);
bool retriedAfterRefresh = false;
RetryChunk:
var request = new HttpRequestMessage(HttpMethod.Post, "https://content.dropboxapi.com/2/files/download");
// Authorization
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", currentAccessToken);
// Dropbox-API-Arg (JSON-encoded)
var arg = new { path = dropboxPath };
string jsonArg = Newtonsoft.Json.JsonConvert.SerializeObject(arg);
request.Headers.Add("Dropbox-API-Arg", jsonArg);
// Range header for chunking
request.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(offset, end);
// Act as team member (Business)
if (!string.IsNullOrEmpty(asMember))
request.Headers.Add("Dropbox-API-Select-User", asMember);
// Apply PathRoot (WithPathRoot) for Business only
if (!string.IsNullOrEmpty(rootNamespaceId))
{
var pathRoot = new Dictionary<string, object>
{
{ ".tag", "root" },
{ "root", rootNamespaceId }
};
string jsonPathRoot = Newtonsoft.Json.JsonConvert.SerializeObject(pathRoot);
request.Headers.Add("Dropbox-API-Path-Root", jsonPathRoot);
}
using (var response = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token))
{
// 🔐 Handle expired token (401)
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
if (retriedAfterRefresh)
{
var err = await response.Content.ReadAsStringAsync();
throw new HttpRequestException(
$"Dropbox download failed after token refresh (401). Body: {err}");
}
// Refresh token based on client type (Personal vs Business)
currentAccessToken = await RefreshAccessTokenAsync(currentAccessToken, baseRequest, isBusinessClient, token).ConfigureAwait(false);
retriedAfterRefresh = true;
goto RetryChunk;
}
// ❌ Other errors
if (!response.IsSuccessStatusCode)
{
var err = await response.Content.ReadAsStringAsync();
throw new HttpRequestException(
$"Dropbox download failed: {(int)response.StatusCode} {response.ReasonPhrase}. Body: {err}");
}
// ✅ Success
using (var stream = await response.Content.ReadAsStreamAsync())
{
fileStream.Seek(offset, SeekOrigin.Begin);
await stream.CopyToAsync(fileStream, bufferSize, token);
}
}
offset = end + 1;
}
}
}
public async Task<DownloadFileResponse> DownloadFileAsync(DownloadFileRequest request)
{
DropboxTeamClient client;
var connectionId = GetDropboxTeamClient(request, out client);
////logsfodebug(request, "DownloadFileAsync");
var account = await client.AsMember(request.AsMember).Users.GetCurrentAccountAsync();
var spaceclient = client.AsMember(request.AsMember).WithPathRoot(new PathRoot.Root(account.RootInfo.RootNamespaceId));
bool newTeam = CheckIfUserIsUpdatedTeam(account);
string tempFilename;
if (request.ExportAs != null)
{
// ===== EXPORT: REVERTED TO OLD SDK CODE (AS REQUESTED) =====
tempFilename = await Retries.PerformWithRetriesAsync(
async () =>
{
var path = request.FilePath.Replace('\\', '/');
var fileInfo = await spaceclient.Files.GetMetadataAsync(path)
.ConfigureAwait(false);
if (fileInfo.IsDeleted)
throw new CannotDownloadFileException(string.Format("File was Deleted. - {0}", path));
long? size = request.Size ?? (long)fileInfo.AsFile.Size;
using (var safeTemporaryFileProvider =
new SafeTemporaryFileProvider(() => PathExtensions.GetTempFileName(request.ContextId)))
{
using (var tokenSource = new CancellationTokenSource(TimeoutHelper.FromSize(size)))
{
// OLD SDK EXPORT CODE (unchanged)
using (var downloadResponse = await spaceclient.Files
.ExportAsync(new ExportArg(request.FilePath, null))
.WithCancellation(tokenSource.Token).ConfigureAwait(false))
{
using (var fileStream = new FileStream(safeTemporaryFileProvider.TempFilename,
FileMode.Create))
{
var downloadStream =
await downloadResponse.GetContentAsStreamAsync()
.WithCancellation(tokenSource.Token)
.ConfigureAwait(false);
await downloadStream.CopyToAsync(fileStream, 4096, tokenSource.Token)
.WithCancellation(tokenSource.Token)
.ConfigureAwait(false);
return safeTemporaryFileProvider.TempFilename;
}
}
}
}
},
ShouldRetryWithDelay).ConfigureAwait(false);
}
else
{
if (newTeam)
{
// ===== BUSINESS / TEAM: NEW HTTP CHUNKED DOWNLOAD =====
tempFilename = await Retries.PerformWithRetriesAsync(
async () =>
{
var path = request.FilePath.Replace('\\', '/');
var fileInfo = await spaceclient.Files.GetMetadataAsync(path).ConfigureAwait(false);
if (fileInfo.IsDeleted)
throw new CannotDownloadFileException(string.Format("File was Deleted. - {0}", path));
long? size = request.Size ?? (long)fileInfo.AsFile.Size;
using (var safeTemporaryFileProvider = new SafeTemporaryFileProvider(() => PathExtensions.GetTempFileName(request.ContextId)))
{
using (var tokenSource = new CancellationTokenSource(TimeoutHelper.FromSize(size)))
{
await DownloadFileInChunksHttpAsync(
request.AccessToken,
path,
safeTemporaryFileProvider.TempFilename,
size ?? 0,
request.AsMember,
account.RootInfo.RootNamespaceId,
tokenSource.Token,
request,
true); // <-- Business client
return safeTemporaryFileProvider.TempFilename;
}
}
},
ShouldRetryWithDelay).ConfigureAwait(false);
}
else
{
// ===== PERSONAL: NEW HTTP CHUNKED DOWNLOAD =====
tempFilename = await Retries.PerformWithRetriesAsync(
async () =>
{
var path = request.FilePath.Replace('\\', '/');
var fileInfo = await client.AsMember(request.AsMember).Files.GetMetadataAsync(path).ConfigureAwait(false);
if (fileInfo.IsDeleted)
throw new CannotDownloadFileException(string.Format("File was Deleted. - {0}", path));
long? size = request.Size ?? (long)fileInfo.AsFile.Size;
using (var safeTemporaryFileProvider = new SafeTemporaryFileProvider(() => PathExtensions.GetTempFileName(request.ContextId)))
{
using (var tokenSource = new CancellationTokenSource(TimeoutHelper.FromSize(size)))
{
await DownloadFileInChunksHttpAsync(
request.AccessToken,
path,
safeTemporaryFileProvider.TempFilename,
size ?? 0,
request.AsMember,
null,
tokenSource.Token,
request,
false); // <-- Personal client
return safeTemporaryFileProvider.TempFilename;
}
}
},
ShouldRetryWithDelay).ConfigureAwait(false);
}
}
var response = new DownloadFileResponse
{
LocalPath = tempFilename
};
return UpdateTokens(response, request, client, connectionId);
}
7 Replies
- DB-Des1 month ago
Dropbox Community Moderator
Thanks for clarifying your request.
I wanted to follow-up on your first enquiry. The .NET SDK does not currently support range retrieval requests, which relates directly to the issue you described with downloading large files (30GB and higher). That said, we've gone ahead and submitted a feature request for the .NET SDK to support Range Retrieval Requests.
We also confirmed that your second request — the ability to retrieve the current access token after it's been refreshed by the SDK — isn't currently supported by the Dropbox .NET SDK either. However, we've submitted a separate feature request for that functionality as well.
While we've passed both requests along to the appropriate teams, we can't make any guarantees on if or when either feature might be implemented.
- Omri19841 month agoExplorer | Level 4
but lets say a token is getting refreshed from the sdk , and now i need to get it for the download , how can i extract it from the dropbox client sdk ?
- Omri19841 month agoExplorer | Level 4
I am fully know how to get the first access token. And my application is running for many years.
And recently we wanted to update the download method with chunks that are not supported in the sdk and used the api. when trying to refresh token all is good and we get a new access token , but when trying to retry the download request with the new access token we can 401. With empty response. No text inside
- DB-Des1 month ago
Dropbox Community Moderator
Yes, that’s correct: refresh tokens do not expire automatically (unless they’re revoked, for example if the user disconnects the app).
Also keep in mind that access tokens do expire after a period of time, even if you obtain them via the SDK. When the access token expires, your app will need to use the refresh token to request a new access token, or the user will need to go through the authorization flow again.
That said, when using the Dropbox NET SDK, you can retrieve the access token as such:
... var tokenResult = await DropboxOAuth2Helper.ProcessCodeFlowAsync( code: code, appKey: AppKey, appSecret: AppSecret, redirectUri: RedirectUri ); Console.WriteLine($"Access token: {tokenResult.AccessToken}"); ... - Omri19841 month agoExplorer | Level 4
i move to 7 but still issue happening
- Omri19841 month agoExplorer | Level 4
Hi,
Thank you for the response.
The response of the 401 is empty.
And I will try to move to version 7 .
So from what you are saying refreshments are stays the same always. Unless you revoked them .
Is there an option to extract the access token from the Dropbox client. Sence I am using both sdk and api . Due to the linitation of download in chunks not available in the sdk .
- DB-Des1 month ago
Dropbox Community Moderator
Hi Omri1984
Refresh tokens don't automatically expire. In general, a refresh token will remain valid unless it’s revoked (for example, if the user disconnects the app, the token is explicitly revoked, or the app's access is otherwise removed).
For the 401 you mentioned, could you share the full error message/response body you're seeing (not just the status code)?
Also, if you haven't already, please make sure you're using the latest version of the SDK, since updates can include important fixes and improvements related to auth handling.
About Dropbox API Support and Feedback
Get help with the Dropbox API from fellow developers and experts.
The Dropbox Community team is active from Monday to Friday. We try to respond to you as soon as we can, usually within 2 hours.
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, Facebook or Instagram.
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!