【问题标题】:OutOfMemoryException when upload file to Google Drive using Google Drive API使用 Google Drive API 将文件上传到 Google Drive 时出现 OutOfMemoryException
【发布时间】:2016-10-06 21:10:37
【问题描述】:

这是一个网络应用程序。我想使用 GoogleDrive API 将大文件上传到 Google Drive。我的代码适用于较小的文件。但是当涉及到较大的文件时,就会发生错误。

我进行了很多搜索,但找不到工作的解决方案以及一些没有答案的胎面。

文件转换为字节[]的代码部分发生错误。

byte[] fileData = System.IO.File.ReadAllBytes(dir);

对于 web.config,我曾尝试更改请求长度,但它不适用于大文件。

<httpRuntime targetFramework="4.5" executionTimeout="520000" maxRequestLength="920000"  />

有人对此有解决方案吗?提前致谢!

以下是完整代码。


using System;
using System.Diagnostics;
using DotNetOpenAuth.OAuth2;
using Google.Apis.Authentication.OAuth2;
using Google.Apis.Authentication.OAuth2.DotNetOpenAuth;
using Google.Apis.Drive.v2;
using Google.Apis.Drive.v2.Data;
using Google.Apis.Util;
using ASPSnippets.GoogleAPI;
using System.Web.Script.Serialization;
using System.Web;
using System.Collections.Generic;
using System.Net.Http;
using System.Net;
using System.IO;
using System.Reflection;

namespace GoogleDriveAutoUpdate
{
    public partial class GoogleDriveSample : System.Web.UI.Page
    {
        static int flagUpdateFile;
        protected string[] dirs = Directory.GetFiles(@"C:\Users\RNKP74\Desktop\GoogleDrive");


        protected void Page_Load(object sender, EventArgs e)
        {
            GoogleConnect.ClientId = "";
            GoogleConnect.ClientSecret = "";
            GoogleConnect.RedirectUri = Request.Url.AbsoluteUri.Split('?')[0];
            GoogleConnect.API = EnumAPI.Drive;

            if (string.IsNullOrEmpty(Request.QueryString["code"]))
                GoogleConnect.Authorize("https://www.googleapis.com/auth/drive.file");

            if (flagUpdateFile == 0)
                UploadFile();
        }

        protected HttpPostedFile ConstructHttpPostedFile(byte[] data, string filename, string contentType = null)
        {
            // Get the System.Web assembly reference
            Assembly systemWebAssembly = typeof(HttpPostedFileBase).Assembly;
            // Get the types of the two internal types we need
            Type typeHttpRawUploadedContent = systemWebAssembly.GetType("System.Web.HttpRawUploadedContent");
            Type typeHttpInputStream = systemWebAssembly.GetType("System.Web.HttpInputStream");

            // Prepare the signatures of the constructors we want.
            Type[] uploadedParams = { typeof(int), typeof(int) };
            Type[] streamParams = { typeHttpRawUploadedContent, typeof(int), typeof(int) };
            Type[] parameters = { typeof(string), typeof(string), typeHttpInputStream };

            // Create an HttpRawUploadedContent instance
            object uploadedContent = typeHttpRawUploadedContent
                .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, uploadedParams, null)
                .Invoke(new object[] { data.Length, data.Length });

            // Call the AddBytes method
            typeHttpRawUploadedContent
                .GetMethod("AddBytes", BindingFlags.NonPublic | BindingFlags.Instance)
                .Invoke(uploadedContent, new object[] { data, 0, data.Length });

            // This is necessary if you will be using the returned content (ie to Save)
            typeHttpRawUploadedContent
                .GetMethod("DoneAddingBytes", BindingFlags.NonPublic | BindingFlags.Instance)
                .Invoke(uploadedContent, null);

            // Create an HttpInputStream instance
            object stream = (Stream)typeHttpInputStream
                .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, streamParams, null)
                .Invoke(new object[] { uploadedContent, 0, data.Length });

            // Create an HttpPostedFile instance
            HttpPostedFile postedFile = (HttpPostedFile)typeof(HttpPostedFile)
                .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, parameters, null)
                .Invoke(new object[] { filename, contentType, stream });

            return postedFile;
        }

        protected void UploadFile()
        {
            int successCount = 0;
            HttpFileCollection MyFileCollection = Request.Files;
            Response.Write("Total number file = " + dirs.Length + "<br/>");

            foreach (string dir in dirs)
            {

                byte[] fileData = System.IO.File.ReadAllBytes(dir);
                string fileName = Path.GetFileName(dir);
                flagUpdateFile = 1;
                //Content Type is null
                Session["File"] = ConstructHttpPostedFile(fileData, fileName);

                //Token return from Google API
                if (!string.IsNullOrEmpty(Request.QueryString["code"]) && flagUpdateFile == 1)
                {
                    string code = Request.QueryString["code"];
                    string json = GoogleConnect.PostFile(code, (HttpPostedFile)Session["File"], "");
                    flagUpdateFile = 0;
                    System.IO.File.Delete(dir);
                    successCount++;
                }

                if (Request.QueryString["error"] == "access_denied")
                {
                    ClientScript.RegisterClientScriptBlock(this.GetType(), "alert", "alert('Access denied.')", true);
                    Console.Write("Access denied,check your Authorized redirect URIs!");
                }

                HttpContext.Current.Session.Remove("File");
                HttpContext.Current.Session.Abandon();

            }
            Response.Write("Total file uploaded = " + successCount);
            successCount = 0;
            Response.Write("<span id='Label1' style='color:red'><br/>Sucess if the number is identical</span>");

        }


    }
}

【问题讨论】:

    标签: c# http google-drive-api bytearray


    【解决方案1】:

    我承认我不知道 GoogleDrive API,但您的代码看起来非常复杂。你为什么要进行反思,为什么要对每个文件进行反思?为什么将整个文件加载到字节数组中只是为了将其放入MemoryStream,而您可以从一开始就使用FileStream,而不会将整个文件复制到内存中?

    我想说你需要简化你的代码。所有的反射?编写您想要编写的实际两行代码。然后使用文件流作为InputStream

    您似乎希望将所有文件放入一个请求中。 API 似乎希望您在一个请求中容纳一个文件。也许你应该这样做。

    【讨论】:

    • 嗨,我为一个文件夹中的每个文件都设置了 foreach,因为我想在一个文件夹中上传多个文件。对于只是一个测试的 MemoryStream,我没有删除,也没有在应用程序中使用。我 foreach 一个文件是因为我想获取一个文件的名称和数据。
    【解决方案2】:

    根据 MSDN,您可以在 web.config 中将其设置为:

    <configuration>
      <runtime>
        <gcAllowVeryLargeObjects enabled="true" />
      </runtime>
    </configuration>
    

    但它说:

    true: Arrays greater than 2 GB in total size are enabled on 64-bit platforms.
    

    所以它不适用于 32 位,这意味着您还需要在 64 位上运行您的池。

    参考:http://msdn.microsoft.com/en-us/library/hh285054.aspx

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-03
    相关资源
    最近更新 更多