处理大文件时报错:内存溢出System.OutOfMemoryException

img

我要从服务器下载一些文件,只允许通过这样的接口方式进行下载;返回的response是一个json我对齐进行反序列化的时候就报错System out of memory了;我研究了好几天,这并不是电脑内存不够的问题,在网上有很多类似疑问,官网也说了会存在这样的问题,但都没有提供解决方法;请问谁有较好的思路或者经验呢?
                    fullpath = dt.Rows[i]["path"].ToString();
                    string fid = dt.Rows[i]["fid"].ToString();
                    clsDownloadFiles returm = new clsDownloadFiles();
                    var client = new RestClient("download_path");
                    client.Timeout = -1;
                    var request = new RestRequest(Method.POST);
                    request.AlwaysMultipartFormData = true;
                    request.AddParameter("accesskeyid", "accesskeyid");
                    request.AddParameter("accesssecret", "accesssecret");
                    request.AddParameter("dstfile", fullpath);
                    request.AddParameter("machineid", "657");
                    //获取文件
                    IRestResponse response = client.Execute(request);
                    clsDownloadFiles dailyResult = Newtonsoft.Json.JsonConvert.DeserializeObject<clsDownloadFiles>(response.Content); 
                    byte[] memoryToString1 = dailyResult.data.data;
                    string filename = dailyResult.data.name.ToString();

200M的json 确实太大了些,接口不能分页去取吗 ,然后再合并

处理大文件时报错:内存溢出System.OutOfMemoryException

代码里不要把数据一次性读入内存,可以进行分片读取

一个文件可能很大,无法一次读取到内存中,所以要分段读取;虽然简单,不过经常用到,就写到这里;

这里为了简单演示,将所有代码在一个函数中完成,具体应用中当然还是要封装一下;



void CTmfc2Dlg::ReadFileT()
{
 // TODO: Add your control notification handler code here
 
 char * pstrFileName = "G:\\176x144.264";

 const int nBufLen = 1000;
 BYTE pbuf[nBufLen]={0};
 
 int nTell = 0;
 int nReadLen = 0;
 static int nFileLen = 0;
 
 static BOOL b = TRUE;

 if ( b == TRUE )//»ñÈ¡Îļþ´óС£»
 {
  FILE * fpFileLen = fopen( pstrFileName, "r+b" );
  fseek( fpFileLen, 0, SEEK_END );
  nFileLen =ftell(fpFileLen);
  fclose(fpFileLen);

  b = 0;
 }

 
 
 
 static FILE * fp = fopen( pstrFileName, "r+b" );
 
 nReadLen = fread( pbuf, 1, nBufLen, fp );
 nTell    = ftell( fp );

 SetDlgItemInt( IDC_EDIT1, nReadLen );
 SetDlgItemInt( IDC_EDIT2, nTell );
 SetDlgItemInt( IDC_EDIT3, nFileLen );

 if ( nTell == nFileLen )
 {
  fclose( fp );
  AfxMessageBox("Îļþ¶ÁÈ¡OK¡£");
 }

}

将文件分包下载,调用端传入文件信息(名称,内容批次,每次长度),服务端按参数读取文件内容返回即可。其实就是数据的分页查询的思路。

返回200M文件用base64编码一下,再通过json格式返回? 这么魔性的API设计也真是醉了,这后端是实习生开发的吧。。。

假设后端没法改的情况下,前端可以这么来做:

1 搜一下 C# http 下载文件,找代码参考
2 得到http响应的Stream
3 创建BufferStream来读Stream(减少系统调用次数)
4 循环读一个字符,直到冒号: 再循环读一个字符,直到引号" (找到实际base64数据的开头)
5 循环:
5.1 读4个非空字符 (如果空格换行则再读下一个字符,如果非base64字符则跳出循环)
5.2 base64解码得到1至3个字节 (前面每段3字节,最后一段可能没有3字节)
5.3 写入解码后的字节到文件

Json.NET 支持从流中反序列化,所以可以将你下载的json字符串转换成二进制流,然后通过JsonReader对象进行反序列化,示例如下:

var json = "你的JSON字符串";
var serializer = new JsonSerializer();
MyObject o;
using (var s = new MemoryStream(Encoding.UTF8.GetBytes(json)))
using (var sr = new StreamReader(s))
using (JsonReader reader = new JsonTextReader(sr))
{
    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            o = serializer.Deserialize<MyObject>(reader);
        }
    }
}

public class MyObject
{
    public string Data { get; set; }
}

完整示例:

using Newtonsoft.Json;
using System;
using System.IO;
using System.Text;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var json = "{\"data\":\"123456\"}"; // 你的JSON字符串
            var serializer = new JsonSerializer();
            var o = new MyObject();
            using (var s = new MemoryStream(Encoding.UTF8.GetBytes(json)))
            using (var sr = new StreamReader(s))
            using (JsonReader reader = new JsonTextReader(sr))
            {
                while (reader.Read())
                {
                    if (reader.TokenType == JsonToken.StartObject)
                    {
                        o = serializer.Deserialize<MyObject>(reader);
                    }
                }
            }
            Console.WriteLine(o.Data);
            Console.ReadKey();
        }
    }

    public class MyObject
    {
        public string Data { get; set; }
    }
}

你好,
建议你使用二进制流。

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

文件切分,多次传输。
然后本地处理。
压缩也可以试试,但是效果不一定。
如果能申请一下更大的内存也行。
或者先存本地,然后用本地的其他方式处理

看一下这边文章对你是否有帮助:https://blog.csdn.net/chilanzi/article/details/125089697?spm=1000.2115.3001.6382&utm_medium=distribute.pc_feed_v2.none-task-blog-hot-18-125089697-null-null.pc_personrec&depth_1-utm_source=distribute.pc_feed_v2.none-task-blog-hot-18-125089697-null-null.pc_personrec

链接:https://pan.baidu.com/s/1AJJQ2wn1z5qFm4WduNHfIQ
提取码:sqmj
我将两个json放在了百度云盘,每个文档里面有个data,就是要将它转换成文件;
两个json文件的data转换生成两个压缩文件
pm_hq_order_spot.7z.001 和pm_hq_order_spot.7z.002
能解压说明就成功了,但是一般在解压pm_hq_order_spot.7z.001就会失败

你先获取数据长度,根据长度初始化一个同样长的变量,用来接收数据。

发生类型为 System.OutOfMemoryException 的异常是设置错误造成的,解决方法为:

1、首先使用百度杀毒软件对全盘进行杀毒操作,杀毒完成后重新安装软件。

2、开始→运行→输入cmd→回车。

3、进入,在命令提示符下输入for %1 in (%windir%\system32*.ocx) do regsvr32 /s %1右键粘贴 回车 。

4、完成后再输入for %1 in (%windir%\system32*.dll) do regsvr32.exe /s %1右键粘贴 回车 即可。

在machine.config配置文件中,的“memoryLimit”属性,这个属性的值是一个百分值,默认是60。对于一台>4G内存的服务器,最好将“memoryLimit”属性设置成“20”,可以调整试下。