上传到 Dropbox 时出错
Getting an error when uploading to Dropbox
我正在尝试将文件上传到保管箱。
这是我到目前为止所说的:
using (var stream = new MemoryStream(File.ReadAllBytes(fileName)))
{
var numChunks = (int) Math.Ceiling((double) stream.Length / chunkSize);
var buffer = new byte[chunkSize];
string sessionId = null;
if (numChunks == 1)
{
using (var memStream = new MemoryStream(buffer, 0, chunkSize))
{
Console.WriteLine($"Sending file: {path}");
var tst = await client.Files.UploadAsync(path, WriteMode.Overwrite.Instance, body: memStream);
}
}
else
{
for (var idx = 0; idx < numChunks; idx++)
{
Console.WriteLine($"Uploading chunk {idx + 1} / {numChunks}.");
var byteRead = stream.Read(buffer, 0, chunkSize);
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
var cursor = new UploadSessionCursor(sessionId, (ulong) (chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
}
}
var url = string.Empty;
var link = await client.Sharing.ListSharedLinksAsync(path);
if (link.Links.Count == 0)
{
var result = await client.Sharing.CreateSharedLinkWithSettingsAsync(path);
url = result.Url;
}
else
{
url = link.Links[0].Url;
}
Console.WriteLine();
Console.WriteLine("Dropbox Download Link:");
Console.WriteLine(url);
Console.WriteLine();
//Console.ReadKey();
}
当我尝试发送一个大文件时,我收到这条消息:
我正在计算大小文件。如果文件大于 chunksize。我无法让它发送文件,我只是收到一个错误。
发生在这一行:
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
有什么建议吗?
对于第一个块 idx==0
,您只需调用 UploadSessionStartAsync
,您还可以调用 UploadSessionAppendV2Async
。试试这个:
...
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
else
{
var cursor = new UploadSessionCursor(sessionId, (ulong) (chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
}
...
不是 运行 在本地,也没有使用 Dropbox 的客户端,但我看到以下...
- 可能,虽然手动调试速度较慢,但您可能会错过问题。
- 预计该错误消息 轻微 具有误导性。总的来说,是对的。但是当您的原始流被更改.
时,应该进行额外的检查以发现这种情况
- 让我添加更多关于 'what the hell is happening here?!.' 的 sync/async 混沌数据。首先,您的
for
运行 同步 - 所有上传会话在短时间内并行开始。 第二,每个 for
都有自己的 block-level scope
,在同步上下文中有 using (...) {...}
语法糖。 所有 var memStream
s 在上层同步上下文中相互覆盖。然后,每个线程在它自己的异步上下文中完成它的工作。看看var response
。它是一个循环变量,并且未在您的主 SYNC 上下文中使用。 它没有在 using
闭包之外声明。 所以这一行在同步上下文中是不可等待的。在这种情况下,using
将在 memStream
被消耗时完成,但 UploadSessionFinishAsync
将继续工作。
- 为什么它只发生在
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
?想一想,这可能是每个 await client.Files...
调用中的问题,但只有 client.Files.UploadSessionFinishAsync
尝试对传递的 memStream
执行某些操作。例如,将其关闭作为最后一步。或处理 memStream
,放弃流,做另一项工作,例如向 Dropbox 请求以获得最终上传结果。
- 关于修复的想法。我认为您需要在
for
范围之上声明最终结果 var response
。然后只需在最后一个块处理时设置结果。在这种情况下 using
将不得不等待它的结果,然后它才能处置 memStream
。就像 sessionId = result.SessionId;
一样。或者只是在 using
结束之前明确等待其结果。
- 代码优化。对于短文件,您不需要
buffer
和 sessionId
。它们用于多个上传线程。对于大文件 - return UploadSessionFinishAsync
来自循环的结果。也许,将多个 memStream
声明为不同的变量。不确定 UploadSessionAppendV2Async
结果。如果是 void
就可以了。否则,这样的结果也需要等待。可能是这样的:
//outer vars
SomeDropboxFileUploadResultOrElse uploadResult;
string sessionId = null;
//original full file read
using (var stream = new MemoryStream(File.ReadAllBytes(fileName)))
{
var numChunks = (int) Math.Ceiling((double) stream.Length / chunkSize);
if (numChunks == 1)
{
Console.WriteLine($"Sending file: {path}");
uploadResult = await client.Files.UploadAsync(path, WriteMode.Overwrite.Instance, body: stream);
}
else
{
var buffer = new byte[chunkSize];
for (var idx = 0; idx < numChunks; idx++)
{
Console.WriteLine($"Uploading chunk {idx + 1} / {numChunks}.");
var byteRead = stream.Read(buffer, 0, chunkSize);
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
//prevent 2+ parts processing on idx == 0 iteration, start thread in the sync scope but wait for sessionId value in the async thread
else
{
var cursor = new UploadSessionCursor(sessionId, (ulong)(chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
//explicitly pass result to outer scope, then the execution could be continued, memStream could be closed below
uploadResult = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
} //memStream?.Dispose();
}
}
...
}
希望这会有所帮助。
我正在尝试将文件上传到保管箱。
这是我到目前为止所说的:
using (var stream = new MemoryStream(File.ReadAllBytes(fileName)))
{
var numChunks = (int) Math.Ceiling((double) stream.Length / chunkSize);
var buffer = new byte[chunkSize];
string sessionId = null;
if (numChunks == 1)
{
using (var memStream = new MemoryStream(buffer, 0, chunkSize))
{
Console.WriteLine($"Sending file: {path}");
var tst = await client.Files.UploadAsync(path, WriteMode.Overwrite.Instance, body: memStream);
}
}
else
{
for (var idx = 0; idx < numChunks; idx++)
{
Console.WriteLine($"Uploading chunk {idx + 1} / {numChunks}.");
var byteRead = stream.Read(buffer, 0, chunkSize);
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
var cursor = new UploadSessionCursor(sessionId, (ulong) (chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
}
}
var url = string.Empty;
var link = await client.Sharing.ListSharedLinksAsync(path);
if (link.Links.Count == 0)
{
var result = await client.Sharing.CreateSharedLinkWithSettingsAsync(path);
url = result.Url;
}
else
{
url = link.Links[0].Url;
}
Console.WriteLine();
Console.WriteLine("Dropbox Download Link:");
Console.WriteLine(url);
Console.WriteLine();
//Console.ReadKey();
}
当我尝试发送一个大文件时,我收到这条消息:
我正在计算大小文件。如果文件大于 chunksize。我无法让它发送文件,我只是收到一个错误。
发生在这一行:
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
有什么建议吗?
对于第一个块 idx==0
,您只需调用 UploadSessionStartAsync
,您还可以调用 UploadSessionAppendV2Async
。试试这个:
...
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
else
{
var cursor = new UploadSessionCursor(sessionId, (ulong) (chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
}
...
不是 运行 在本地,也没有使用 Dropbox 的客户端,但我看到以下...
- 可能,虽然手动调试速度较慢,但您可能会错过问题。
- 预计该错误消息 轻微 具有误导性。总的来说,是对的。但是当您的原始流被更改. 时,应该进行额外的检查以发现这种情况
- 让我添加更多关于 'what the hell is happening here?!.' 的 sync/async 混沌数据。首先,您的
for
运行 同步 - 所有上传会话在短时间内并行开始。 第二,每个for
都有自己的block-level scope
,在同步上下文中有using (...) {...}
语法糖。 所有var memStream
s 在上层同步上下文中相互覆盖。然后,每个线程在它自己的异步上下文中完成它的工作。看看var response
。它是一个循环变量,并且未在您的主 SYNC 上下文中使用。 它没有在using
闭包之外声明。 所以这一行在同步上下文中是不可等待的。在这种情况下,using
将在memStream
被消耗时完成,但UploadSessionFinishAsync
将继续工作。 - 为什么它只发生在
var response = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
?想一想,这可能是每个await client.Files...
调用中的问题,但只有client.Files.UploadSessionFinishAsync
尝试对传递的memStream
执行某些操作。例如,将其关闭作为最后一步。或处理memStream
,放弃流,做另一项工作,例如向 Dropbox 请求以获得最终上传结果。 - 关于修复的想法。我认为您需要在
for
范围之上声明最终结果var response
。然后只需在最后一个块处理时设置结果。在这种情况下using
将不得不等待它的结果,然后它才能处置memStream
。就像sessionId = result.SessionId;
一样。或者只是在using
结束之前明确等待其结果。 - 代码优化。对于短文件,您不需要
buffer
和sessionId
。它们用于多个上传线程。对于大文件 - returnUploadSessionFinishAsync
来自循环的结果。也许,将多个memStream
声明为不同的变量。不确定UploadSessionAppendV2Async
结果。如果是void
就可以了。否则,这样的结果也需要等待。可能是这样的:
//outer vars
SomeDropboxFileUploadResultOrElse uploadResult;
string sessionId = null;
//original full file read
using (var stream = new MemoryStream(File.ReadAllBytes(fileName)))
{
var numChunks = (int) Math.Ceiling((double) stream.Length / chunkSize);
if (numChunks == 1)
{
Console.WriteLine($"Sending file: {path}");
uploadResult = await client.Files.UploadAsync(path, WriteMode.Overwrite.Instance, body: stream);
}
else
{
var buffer = new byte[chunkSize];
for (var idx = 0; idx < numChunks; idx++)
{
Console.WriteLine($"Uploading chunk {idx + 1} / {numChunks}.");
var byteRead = stream.Read(buffer, 0, chunkSize);
using (var memStream = new MemoryStream(buffer, 0, byteRead))
{
if (idx == 0)
{
var result = await client.Files.UploadSessionStartAsync(body: memStream);
sessionId = result.SessionId;
}
//prevent 2+ parts processing on idx == 0 iteration, start thread in the sync scope but wait for sessionId value in the async thread
else
{
var cursor = new UploadSessionCursor(sessionId, (ulong)(chunkSize * idx));
if (idx == numChunks - 1)
{
Console.WriteLine($"Finalizing file: {path}");
//explicitly pass result to outer scope, then the execution could be continued, memStream could be closed below
uploadResult = await client.Files.UploadSessionFinishAsync(cursor, new CommitInfo(path), memStream);
}
else
{
await client.Files.UploadSessionAppendV2Async(cursor, body: memStream);
}
}
} //memStream?.Dispose();
}
}
...
}
希望这会有所帮助。