【问题标题】:Atomic, asynchronous HTTP file POST with sensible feedback?带有合理反馈的原子、异步 HTTP 文件 POST?
【发布时间】:2009-05-10 23:02:21
【问题描述】:

我刚刚开始在 Mac 上进行开发,发现 Cocoa 是一个有用且周到的框架,但它的 HTTP 功能让我感到困惑。

我有一个 NSURLConnection 对象,可以使用 HTTP GET 方法从我的网络服务器下载文件。 NSURLConnect 的异步连接很棒,我得到了很多反馈,我将每个块作为一个新的 NSData 对象接收,我可以使用它在客户端自动重建文件,重要的是,为用户提供进度报告:[myData length ].

然而,上传远没有那么整洁。您可以将同步请求粘贴在其自己的线程中,也可以调用异步请求(我相信它会产生自己的线程),但都不会为您提供任何有用的反馈。没有代表请求数据,甚至没有让我知道何时发送数据。大概这会限制我使用小于可用内存的文件。

因此,我的问题是,是否有一个简单而优雅的解决方案来使用 Cocoa 上传 HTTP POST 文件,该解决方案提供大量反馈和逐部分读取文件的能力,而不是一次全部读取?或者我应该使用低级网络功能编写自己的类?

谢谢!

【问题讨论】:

  • 为什么不让他们进入自己的线程?您可以使用 NSOperation 来确保您没有创建/管理/等。大量不需要的线程。您希望从 POST 操作中获得什么样的数据反馈?
  • 到目前为止我上传的数据量。我正在处理可能很大的文件,所以我需要为用户提供一个进度条或类似的东西。线程不是问题,它更多的是能够逐部分加载文件并获得反馈。支持荒谬的快速回复!
  • 啊,好吧!我可能会自己滚动一些东西。我很确定我曾经看到过一个不错的具有 POST 功能的 NSURL 类,但我不记得在哪里......也许有人会添加一个不错的答案,但你可能想用谷歌搜索一下。我记得我自己花了一点时间才找到它。
  • 好的,这个怎么样:mactech.com/articles/mactech/Vol.19/19.04/CocoaApplications/… -- 这绝对是你自己的,但它似乎是经过深思熟虑的,还有其他人为你推出了它:) 看起来应该很容易也可以满足您的需求...
  • 如果您从磁盘上的文件上传内容,请务必查看 CFHTTPMessage API(在 iPhone 上的 CFNetwork.framework 中,Mac 上的 CoreServices.framework 中)。这允许您为请求的主体数据指定一个流,而不是一大块数据。这样,您可以直接从文件中实例化一个 NSInputStream(或 CFReadStreamRef),并将其作为其主体提供给 CFHTTPMessage。

标签: objective-c cocoa http macos upload


【解决方案1】:

您可能想查看ASIHTTPRequest 框架。我没有用它来上传,但它看起来有更多的反馈,并且使用起来非常简单。

【讨论】:

    【解决方案2】:

    我决定使用 CFNetwork 函数而不是 NSURLConnection。异步通知和特定功能(例如身份验证)似乎更具灵活性。不幸的是,它有点复杂(例如运行循环让我大吃一惊),所以如果你走这条路,我建议你阅读 CFNetwork 参考指南:

    http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/Introduction/Introduction.html

    这是我的 POST 例程 FWIW 中的一段代码:

    // Create our URL
    CFStringRef url = CFSTR("Submit");
    CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, baseUrl);
    
    // Create the message request (POST)
    CFStringRef requestMethod = CFSTR("POST");
    CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, requestMethod, myURL, kCFHTTPVersion1_1);
    
    // Connect the read socket to the HTTP request stream
    CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, myRequest, readStream);
    // TODO: why does this have to be done?
    succ &= CFReadStreamSetClient(myReadStream,
      kCFStreamEventOpenCompleted | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered,
      (CFReadStreamClientCallBack) &MyReadCallBack, &myClientContext);
    CFReadStreamScheduleWithRunLoop(myReadStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
    succ &= CFReadStreamOpen(myReadStream);
    

    【讨论】:

      【解决方案3】:

      ASIHTTPRequest 最初是为此目的而设计的(跟踪 POST 进度),因为在 2.x API 中,这对于 NSURLConnection 是不可能的。它肯定会比使用 CFNetwork 自行集成更容易,并且您可以免费获得许多其他东西(例如跨多个请求的进度跟踪、恢复下载等)。 :)

      如果您要上传的文件很大,请务必查看直接从磁盘流式传输的选项,这样您就不必将数据保存在内存中。

      【讨论】:

        【解决方案4】:

        不幸的是,您说 NSURLConnection 在这里很弱是完全正确的。我推荐的最灵活的方法是CocoaAsyncSocket。这意味着滚动你自己的 HTTP,这很不幸,但在大多数情况下并不那么困难。 CocoaHTTPServer 演示了如何在 CocoaAsyncSocket 之上构建一个完整的 HTTP 服务器,并且可能有很多有用的代码可以解决您的问题。我发现这两个都非常有用。

        另一种可能值得研究的方法是 WebKit。创建一个不可见的 WebView,然后 loadRequest: 一个 POST。估计的变更通知系统是包含上传时间还是只包含下载时间我还没有深入研究过,但值得一试。

        【讨论】:

          【解决方案5】:

          您可以查看my toolkit repository on github 的 HTTPMessage 部分,以获取围绕 CFHTTPMessageRef 的简单 ObjC 包装器;除其他外,它会给你一个 NSInputStream 对象,这样你就不用考虑纯 C 回调函数了。

          根据您阅读的内容,您可能需要查看 XML(和 HTML)解析器的同一存储库的 StreamingXMLParser 部分,该解析器将代表您直接从所述 NSInputStream 解析数据。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-06-18
            • 2019-03-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多