ios的文件怎么上传到服务器地址,iOS如何通过流式传输将大型资产文件上传到服务器...

假设您的数据太大而无法放入内存:

一种有效且可靠的方法将利用一对有界CFStream(见CFStreamCreateBoundPair).

有界流对的输入流设置为NSMutableURLRequest的HTTPBodyStream属性.有界流对的输出流将用于写入从固定大小的内存缓冲区获得的字节,该缓冲区已填充ALAssetRepresentation的getBytes:fromOffset:length:error:方法.

有界流对的传输缓冲区的大小应与资产表示的缓冲区大小相同.

设置代码需要几行代码和NSStreams的一些经验并处理事件(NSStreams经常有一些细微之处).

这种方法的工作原理如下:

>创建一个处理所有流事件的流委托.

>为传输缓冲区设置具有特定大小的配对流,设置委托并在运行循环上安排它们.

>为具有相同大小的资产数据设置内存缓冲区.

>打开流时,会收到NSStreamEventHasSpaceAvailable事件.您可以通过getBytes:fromOffset:length:error:读取资产数据来处理该事件,并写入内存缓冲区.当您使用一大块资产数据填充缓冲区时,请将此缓冲区写入有界流对的输出流.适当地跟踪偏移!

>现在,有界流对的输入流很好地由底层连接提取(它将字节从内部传输缓冲区移动到网络套接字),并且您将获得另一个NSStreamEventHasSpaceAvailable事件,因为现在内部传输中有空间 – 缓冲.写入适合从资产数据缓冲区到输出流的有界流对输出流的字节数,以及资产数据缓冲区中可用的字节数.如果资产数据缓冲区已完全写入,请重新填充.仔细跟踪偏移和范围!

>您处理事件,直到写入整个资产数据.然后关闭输出流.

>您还需要处理其他流事件,请参阅:Stream Programming Guide

注意:您可能会注意到您的内存缓冲区只能部分写入输出流.通过跟踪偏移来处理这一问题,以便始终在缓冲区中保留连续的资产数据流,并将适当的数据范围从缓冲区写入输出流!

警告:

使用有限的一对流设置正确的实现可能很棘手并且可能容易出错.我有一个通用版本的“InputStreamSource”(它暴露了一个普通的NSInputStream,它将用于设置HTTPBodyStream属性),它可以很容易地扩展为与任何输入源一起使用 – 例如资产数据.如果你有兴趣,我可以把这个代码放在gist上.

AFNetworking或任何其他网络库都无法解决这个问题.说实话,我不建议将AFNetworking与流一起用作身体部分 – 因为AFNetworking的实施在这方面仍然是可疑的.我建议使用NSURLConnection自己实现委托,或者使用另一个第三方库来正确处理POST请求的输入正文流.

短(不完整)示例

这个想法是,创建某种“资产输入源”类,它暴露一个NSInputStream(可以用来设置NSURLRequest的HTTPBodyStream属性)并提供资产数据.

如果“资产输入源”是文件,则任务很简单:只需创建与该文件关联的NSInputStream对象.但是,我们的资产只能通过某种表示形式的字节范围访问,该字节存在于某个临时缓冲区中.

因此,任务是用适当的字节范围填充该临时缓冲区.然后,分段,将这些字节写入绑定到输入流的专用输出流.此输入流和输出流对将通过函数CFStreamCreateBoundPair创建.

输入流将成为我们公开的“资产输入源”的NSInputStream.

输出流仅在内部使用. “资产输入源”将使用资产进行初始化.

我们的“资产输入源”类需要处理流事件,因此它将成为流委托.

现在,我们有了实现它的一切.

CFStreamCreateBoundPair函数创建CFStream对象.但是,由于NSStream是toll-free bridged,我们可以轻松地将它们“转换”为NSStreams.

“资产输入源”类的start或init方法的一部分可以实现如下:

_buffer = (uint8_t)malloc(_bufferSize);

_buffer_end = _buffer + _bufferSize;

_p = _buffer;

CFReadStreamRef readStream = NULL;

CFWriteStreamRef writeStream = NULL;

CFStreamCreateBoundPair(NULL,&readStream,&writeStream,_bufferSize);

self.inputStream = CFBridgingRelease(readStream);

self.internalOutputStream = CFBridgingRelease(writeStream);

[self.internalOutputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:self.runLoopMode];

[self.internalOutputStream open];

// (Note: inputStream will be opened and scheduled by the request!)

inputStream是该类的公共@property(公开的输入流).

internalOutputStream是类的私有属性.

_buffer是内部缓冲区,包含资产表示的字节范围.

请注意,有界流对的内部缓冲区大小等于保存资产数据的缓冲区.

流委托方法流:handleEvent:可以实现如下所示:

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent

{

if (_isCancelled) {

return;

}

switch (streamEvent) {

case NSStreamEventNone:

break;

case NSStreamEventOpenCompleted:

DLogInfo(@"internal output stream: open completed");

break;

case NSStreamEventHasBytesAvailable:

// n.a.

NSAssert(0,@"bogus stream event");

break;

case NSStreamEventHasSpaceAvailable:

NSAssert(theStream == _internalOutputStream,@"bogus stream event");

DLogInfo(@"destination stream: has space available");

[self write];

break;

case NSStreamEventErrorOccurred:

DLogInfo(@"destination stream: error occurred");

[self finish];

break;

case NSStreamEventEndEncountered:

// weird error: the output stream is full or closed prematurely,or canceled.

DLogWarn(@"destination stream: EOF encountered");

if (_result == nil) {

self.result = [NSError errorWithDomain:NSStringFromClass([self class])

code:-2

userInfo:@{NSLocalizedDescriptionKey: @"output stream EOF encountered"}];

}

[self finish];

break;

}

}

如您所见,秘密在于方法写入.还有完成方法和取消方法.

基本上,方法将_buffer中的副本写入内部输出流,尽可能多地放入流中.当_buffer完全写入输出流时,它将再次从资产数据中填充.

当没有可用于从资产写入输出流的数据时,将调用方法完成.

方法完成关闭内部输出流并取消调度流.

完整可靠的实现可能有点棘手. “资产输入源”也应该是可取消的.

如上所述,我确实有一个“抽象输入源”类,它实现除了使用资产数据填充_buffer之外的所有内容,如果需要,我可以将其作为Gist上的代码片段提供.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值