从 Xamarin Android 应用程序崩溃将大文件上传到 WCF

Posted

技术标签:

【中文标题】从 Xamarin Android 应用程序崩溃将大文件上传到 WCF【英文标题】:Uploading Large Files to WCF from Xamarin Android App Crashes 【发布时间】:2020-09-21 15:01:01 【问题描述】:

我正在尝试从我的 xamarin 应用程序上传一个大视频 (1 GB+),一旦达到我的文件的大约 0.5 GB,它就会一直崩溃。我发现在发送数据的同时将视频发布到我的 WCF 服务的唯一方法是使用 MultiPart 逻辑,但我不确定我是内存不足还是什么,因为即使在调试模式下,它只是崩溃而没有任何真正的错误消息。

我正在尝试在本机设备(不是 sim)上运行它,它是搭载 android 9 的三星 Galaxy S9。

这是我正在使用的上传代码:(ps - 作为测试,我尝试将 WriteAsync 放入 for 循环中,认为可能尝试编写整个演出是问题所在,但结果是相同的。那是为什么你会在那里看到 MAXFILESIZEPART 常量,它只是一个等于 10000000 的 int。)

private async Task<byte[]> GetMultipartFormDataAsync(Dictionary<string, object> postParameters, string boundary)
        
            try
            

                using (Stream formDataStream = new System.IO.MemoryStream())
                
                    bool needsCLRF = false;

                    foreach (var param in postParameters)
                    
                        // Thanks to feedback from commenters, add a CRLF to allow multiple parameters to be added.
                        // Skip it on the first parameter, add it to subsequent parameters.
                        if (needsCLRF)
                            await formDataStream.WriteAsync(Encoding.UTF8.GetBytes("\r\n"), 0, Encoding.UTF8.GetByteCount("\r\n"));

                        needsCLRF = true;

                        if (param.Value is FileParameter)
                        
                            FileParameter fileToUpload = (FileParameter)param.Value;

                            // Add just the first part of this param, since we will write the file data directly to the Stream
                            string header = string.Format("--0\r\nContent-Disposition: form-data; name=\"1\"; filename=\"2\"\r\nContent-Type: 3\r\n\r\n",
                                                boundary,
                                                param.Key,
                                                fileToUpload.FileName ?? param.Key,
                                                fileToUpload.ContentType ?? "application/octet-stream");

                            await formDataStream.WriteAsync(Encoding.UTF8.GetBytes(header), 0, Encoding.UTF8.GetByteCount(header));

                            // Write the file data directly to the Stream, rather than serializing it to a string.
                            if (fileToUpload.File.Length > MAXFILESIZEPART)
                            
                                for (var i = 0; i < fileToUpload.File.Length; i += MAXFILESIZEPART)
                                
                                    var len = i + MAXFILESIZEPART > fileToUpload.File.Length
                                        ? fileToUpload.File.Length - i
                                        : MAXFILESIZEPART;
                                    await formDataStream.WriteAsync(fileToUpload.File, i, len);
                                
                            
                            else
                            
                                await formDataStream.WriteAsync(fileToUpload.File, 0, fileToUpload.File.Length);
                            

                        
                        else
                        
                            string postData = string.Format("--0\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2",
                                                  boundary,
                                                  param.Key,
                                                  param.Value);
                            await formDataStream.WriteAsync(Encoding.UTF8.GetBytes(postData), 0, Encoding.UTF8.GetByteCount(postData));
                        
                    

                    // Add the end of the request.  Start with a newline
                    string footer = "\r\n--" + boundary + "--\r\n";
                    await formDataStream.WriteAsync(Encoding.UTF8.GetBytes(footer), 0, Encoding.UTF8.GetByteCount(footer));

                    // Dump the Stream into a byte[]
                    formDataStream.Position = 0;
                    byte[] formData = new byte[formDataStream.Length];
                    formDataStream.Read(formData, 0, formData.Length);

                    return formData;
                

            
            catch (Exception e)
            
                Console.WriteLine(e);
                throw;
            
        

它最终在下一行失败了

await formDataStream.WriteAsync(fileToUpload.File, i, len);

但只有在某个点之后(大约 500MB),所以我假设这是内存问题,但事实并非如此。有没有更好的方法来完成这项任务?我这样做是为了在上传时记录进度。我正在尝试完成类似于通过 facebook 应用程序上传大型视频的操作,以便在您继续工作时在后台上传。它在处理较小的文件(即 -

注意:这发生在它开始向服务器发布任何内容之前,因此它与 IIS 或 WCF 无关。此代码仅将字节写入内存流就会崩溃。

有什么建议吗?

谢谢!

【问题讨论】:

IIS/WCF 的许多配置设置限制了请求的大小。我建议你看看那些。我还会检查 IIS 日志。 这发生在它开始向服务器发布任何内容之前。此代码在将数据写入内存流时崩溃。 我不确定将 1 GB 的文件作为单个请求传输是否会很好地工作 - 无论您是否修复此特定错误 【参考方案1】:

根据你的描述,服务会在某个时间点停止,并且因为你传输的文件是1G左右,很可能是sendtimeout。在指定时间内没有完成传输,导致异常。指定的SendTimeout写操作在超时之前必须完成多长时间。默认值为 1 分钟。

我在我的配置文件中将sendtimeout设置为15秒。如果数据耗时超过15秒,就会出现异常。您可以将其设置为更高的值以避免超时和异常。

有关发送超时的信息,请参考以下链接:

https://docs.microsoft.com/en-us/dotnet/api/system.servicemodel.channels.binding.sendtimeout?view=dotnet-plat-ext-3.1

更新

我认为可能是内存溢出问题。大文件可能导致内存溢出,无法同时读取。

解决方法可以参考以下链接

https://docs.microsoft.com/en-us/archive/blogs/johan/are-you-getting-outofmemoryexceptions-when-uploading-large-files

【讨论】:

这发生在它开始向服务器发布任何内容之前。此代码在将数据写入内存流时崩溃。 我更新我的回复,我觉得可能是内存溢出问题。

以上是关于从 Xamarin Android 应用程序崩溃将大文件上传到 WCF的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin 试图从 web 服务中获取用户,立即崩溃

Xamarin.Android 应用程序在选中“共享运行时选项”时崩溃

本机 Xamarin.Android 应用程序在发布模式下不断崩溃

Samsung / Android 8.0 Oreo 更新导致应用程序崩溃? (Xamarin.Forms 应用程序)

Xamarin PCL Android 应用程序在发布模式下突然崩溃

在Pre-Lollipop设备中使用.xml向量时,应用程序崩溃Xamarin.Android