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);
}