【问题标题】:IOS NSURLSession Background Uploading how to upload Large Files in Background?IOS NSURLSession 后台上传如何在后台上传大文件?
【发布时间】:2017-04-24 14:34:55
【问题描述】:

我想将 2-3 GB 的大文件(视频)上传到后台服务器,要求如下

第一种方法

  1. 如果互联网连接丢失并重新连接,则应继续上传

  2. 即使应用程序在后台,也应该继续上传

  3. 如果用户杀死应用程序并返回,上传应该恢复

对于我已经实现的上述功能

  1. 用户选择文件

  2. 将文件拆分成 1MB 的 Chunks,并将磁盘上的所有 Chunks 保存为文件

  3. 针对每个块文件创建上传任务并在后台会话中添加文件

上述方法有效,但在某些情况下失败

  1. 如果文件大于 1GB 在磁盘上创建块和写入块 抛出内存异常

  2. 如果我想上传 1GB 的文件,我需要额外的 1GB 空间来创建块

第二种方法

在不创建块的情况下上传原始文件,在这种情况下,如果网络连接丢失或用户终止应用程序,我将无法继续上传

我的问题是在后台上传大文件的最佳方式是记住所有这些要点

我知道已经有人问过这类问题,但没有人回答我的问题

我花了很多时间来实现它但无法成功实现它请帮助我或提出一些建议来完成上述几点的最佳方法是什么

更新

我正在使用以下代码创建块 代码在 Xamarin.IOS 中,但如果有人在 Objective C 或 Swift 中提供解释,我很好

public static void SplitFileInChunks( UploadFileInfo UploadFile )
{
        int i = -1;

        long chunkSize = UploadHelper.chunkSize;
        nuint dataLength = (System.nuint)chunkSize; 

        //var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        string directoryPath = UploadHelper.UploadsDirectory;

        int chunkCount = 0;
        NSFileHandle fileHandleRead = NSFileHandle.OpenRead(UploadFile.FilePath);

        fileHandleRead.ReadInBackground();
        //fileHandleRead.WaitForDataInBackground();

        if (fileHandleRead == null)
            return;

        do
        {
            i++;
            ulong index = (ulong)(i * chunkSize);

            var filePath = Path.Combine(directoryPath, UploadFile.ContentGuide + "" + i.ToString());
            //fileHandleRead.SeekToFileOffset(index);

            NSData data = fileHandleRead.ReadDataOfLength(dataLength );
            Console.WriteLine(UploadFile.FileStatus);

            if (data.Length <= 0)
                continue;

            NSFileManager.DefaultManager.CreateFile(filePath, data, attr: null);


            NSError error;
            //data.Save(filePath, true, out error);

            chunkCount++;

            Console.WriteLine("Data Lenght" + data.Length);
            data.Dispose();
            Console.WriteLine("Chunk " + i);
        }

        while ( i * chunkSize <= UploadFile.Size && UploadFile.FileStatus != UploadFileStatus.Aborted );

        fileHandleRead.CloseFile();
        fileHandleRead.Dispose();

        Console.WriteLine("All Files Written sucessuflly");
        UploadFile.TotalChunksCount = chunkCount;

    }

【问题讨论】:

  • 是的,我正在使用后台会话,但要在磁盘上保存块我需要额外的空间,因为据我所知,后台的 NSURLsession 仅适用于 NSURL.FromFileName
  • 那么有没有其他方法可以克服这个问题,比如上传原始文件并支持恢复
  • 我已尝试将其添加到 Auto ReleasePool 块中,但没有成功

标签: ios nsurlsession nsurlsessionuploadtask


【解决方案1】:

这肯定行得通,但如果你能控制另一端的软件,你可以做得更好:

  • 在服务器端:

    • 提供仅提供唯一 ID 的上传开始端点 (URL)。
    • 提供一个上传数据端点,该端点采用唯一 ID、POST 正文和可选的起始字节偏移量,并将数据写入服务器上的临时文件。
    • 提供一个上传状态端点,该端点采用唯一 ID 并返回它迄今为止存储在磁盘上的数据量。
    • 提供上传完成的端点。
  • 在客户端:

    • 调用起始端点并获取上传 ID。
    • 调用上传数据端点并开始发送数据。
    • 失败时,调用上传状态端点以了解服务器实际获取了多少数据。
    • 然后调用数据端点并开始从该偏移量发送数据,告诉服务器您从哪里开始。 (在服务器上,始终从该偏移量开始写入文件,即使从那时起长度增加了,为了安全起见。)
    • 完成后,调用上传完成端点。

这种架构还可以相当轻松地显示状态栏。

【讨论】:

  • 不幸的是,除非您的数据完全适合 RAM,否则我认为无法避免创建文件的截断副本。如果您对其进行预分块,则可以创建当前块的截断副本。无论哪种方式,您的应用都必须被唤醒并处理错误并重新启动传输。在某些时候,如果您的应用过于频繁地被唤醒,您将开始受到 NSURLSession 的惩罚,因此分块可能比不分块更糟糕。
  • 实际上,现在我想起来了,您可能能够使用基于流上传的 NSURLSessionDownloadTask,但这取决于守护进程是否正确处理。首先尝试该方法,但如果失败,则必须创建文件的截断副本..
  • 我没有明白第一种方法是什么意思,其次我尝试使用这种方法制作截断的副本stackoverflow.com/questions/28620522/…,但不幸的是,如果文件大约为 2-3 GB,应用程序就会崩溃,我有只上传了几个字节
  • 创建下载任务时,可以指定一个包含NSStreamNSURLRequest对象。如果您使用getBoundStreamsWithBufferSize:inputStream:outputStream:,您的应用程序可以控制提供正文数据。然后,一次从文件中读取几兆字节的数据并将其写入输出流,并在创建NSURLRequest 时传递输入流。基本上,在尝试发送数据之前打开流进行写入,等到收到“可用空间”事件,写入一些数据,然后检查返回值以确定实际发送了多少数据。
  • 写完最后一个chunk后一定要关闭流,否则上传永远不会完成。
猜你喜欢
  • 2014-12-26
  • 1970-01-01
  • 2013-11-27
  • 2020-03-16
  • 2021-06-21
  • 2015-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多