大型视频文件无法通过WebClient上传到WebApi.UploadFileTaskAsync

时间:2016-09-21 15:25:14

标签: c# xamarin.ios asp.net-web-api2 xamarin.forms webclient

我正在使用Xamarin构建iOS(以及后来的Android)应用程序,该应用程序允许用户录制视频并使用POST将其上传到服务器到我已有的Web API。

当我在调试模式下在设备上运行应用程序时,我可以看到正在上传的文件的进度,但它最终会在文件完全上载之前停止。但是,它没有例外,应用程序只是退出。应用程序输出中没有任何内容,iOS日志中没有任何内容,调试器中没有错误。它刚刚消失。

我已经使用Fiddler通过POST将类似大小的文件发送到同一个Web API端点,它运行正常。我无法通过应用程序上传大约20MB的任何内容,但可以使用Fiddler成功上传30MB,50MB和更大的文件。

我的Web.Config文件设置为允许在RequestFiltering和HttpRuntime中上传100MB文件。

我还能在其他任何地方看到导致此次崩溃的原因吗?从我测试过的内容来看,我似乎将它与WebClient.UploadFileTaskAsync方法调用隔离开来,并且似乎大约是21 MB崩溃开始发生的地方。文件> 21 MB在上传过程中从10-20MB失败,而20MB文件将一直到20MB(比较大文件失败的地方还要多)。

这是我的文件上传器依赖服务:

[assembly: Dependency (typeof (FileUploader_iOS))]
namespace GbrApps.iOS
{
public class FileUploader_iOS: IFileUploader
{
    public FileUploader_iOS ()
    {
    }
    public async Task<GbrAppVideo> UploadFileAsync(string FileName, int VideoId)
    {
        try
        {
        //Prepare to make a client ot the API
        WebClient client = new WebClient ();

        client.UploadProgressChanged += (object sender, UploadProgressChangedEventArgs e) => 
        {
            //Upload progress changed as you see fit.
            Console.WriteLine("Progress: {0}, Sent {1}, Total {2}",e.ProgressPercentage,e.BytesSent,e.TotalBytesToSend);
            GlobalResources.UploadProgress = e.ProgressPercentage;
        };

        //Get the file type, because the receiving end receives a generic "bodypart" type of file.
        string[] splitFileName = FileName.Split('.');
        string FileType = splitFileName.Last ().ToLower ();

        //Prep the file upload URL
        //Make sure your receiving end can handle big attachments if you're sending photos or video.
        //ASP.NET can only handle 4MB files by 
        Uri destination = new Uri(string.Format("http://####.com/api/videos?VideoId={0}&FileType={1}&OS=IOS",VideoId,FileType));
        Console.WriteLine ("Uploading to " + destination.ToString ());

        //Send the file and wait for a response asyncronously
        byte[] byteResponse = await client.UploadFileTaskAsync(destination,FileName);
        string response = System.Text.Encoding.UTF8.GetString (byteResponse);

        //Parse the response as a JSON object
        GbrAppVideo result = JsonConvert.DeserializeObject<GbrAppVideo>(response);
        Console.WriteLine (string.Format ("Upload completed: {0}", result));

        //Delete the local file since we don't need it anymore
        if (result.VideoId >0) {
            File.Delete (FileName);
        }

        //Let the system know the video is done
        GlobalResources.activeVideo = result;

        //Return the uploader result object so code can continue and know what to do.
        return result;
            }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
            GlobalResources.activeVideo = new GbrAppVideo();
            return GlobalResources.activeVideo;
        }
    }
}
}
编辑:我发现我可以在我的模拟器上测试这个,通过上传一个更大的样本视频文件而不是我用作占位符的非常小的相机,当相机不可用时。应用程序仍然崩溃(不是在调试器中,只是完全锁定)但我得到了这个未被捕获的异常(即使我有一个try / catch块):

System.Net.WebException: An exception occurred during a WebClient request.
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Users/builder/data/lanes/3539/f37444ae/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:143 
  at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00047] in /Users/builder/data/lanes/3539/f37444ae/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:187 
  at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x0002e] in /Users/builder/data/lanes/3539/f37444ae/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156 
  at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x0000b] in /Users/builder/data/lanes/3539/f37444ae/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128 
  at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in /Users/builder/data/lanes/3539/f37444ae/source/maccore/_build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/src/mono/external/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:357 
  at GbrApps.iOS.FileUploader_iOS+<UploadFileAsync>c__async0.MoveNext () [0x00132] in /Users/chetcromer/Projects/GbrApp/GbrApps/iOS/gbrApi/FileUploader_iOS.cs:52 

这引用了这行代码,这是上传发生的地方......

byte[] byteResponse = await client.UploadFileTaskAsync(destination,FileName);

编辑:

我尝试将我的上传代码行更改为不运行异步并运行正常,但我现在必须等待上传完成才能继续使用该应用程序(不可接受):

byte[] byteResponse = client.UploadFile(destination, FileName);

我想知道我是否发生超时,即使我阅读的文档显示我必须调用和Abort方法,并且没有自动超时异步电话......但看起来确实发生了什么。

编辑:我通过移动到UploadFile而不是UploadFileTaskAsync并将整个内容包装在await Task.Run中来实现这一点。我不再能够访问上传进度事件,但它确实完成了任务。如果可以的话,我想回到最初的方法,但在找到导致异常的原因后我才能被困,这对我有用,不会对用户造成影响。

await Task.Run(() =>
            {
                byte[] byteResponse = client.UploadFile(destination, FileName);
                string response = System.Text.Encoding.UTF8.GetString(byteResponse);

                //Parse the response as a JSON object
                result = JsonConvert.DeserializeObject<GbrAppVideo>(response);
                Console.WriteLine(string.Format("Upload completed: {0}", result));

                //Delete the local file since we don't need it anymore
                if (result.VideoId > 0)
                {
                    File.Delete(FileName);
                }

                //Let the system know the video is done
                GlobalResources.activeVideo = result;
            }

1 个答案:

答案 0 :(得分:3)

这不是我正在寻找的真正答案,但它确实有效。我仍然希望让UploadFileTaskAsync正常工作,但它似乎已超时,所以我将UploadFile()包装在Task.Run()中以使其正常工作(但是没有进度指示器):

我通过移动到UploadFile而不是UploadFileTaskAsync并将整个内容包装在await Task.Run中来实现这一点。我不再能够访问上传进度事件,但它确实完成了任务。如果可以的话,我想回到最初的方法,但在找到导致异常的原因后我才能被困,这对我有用,不会对用户造成影响。

await Task.Run(() =>
        {
            byte[] byteResponse = client.UploadFile(destination, FileName);
            string response = System.Text.Encoding.UTF8.GetString(byteResponse);

            //Parse the response as a JSON object
            result = JsonConvert.DeserializeObject<GbrAppVideo>(response);
            Console.WriteLine(string.Format("Upload completed: {0}", result));

            //Delete the local file since we don't need it anymore
            if (result.VideoId > 0)
            {
                File.Delete(FileName);
            }

            //Let the system know the video is done
            GlobalResources.activeVideo = result;
        }