gpt4 book ai didi

Swift - URLSessionUploadTask 在将整个流写入请求正文之前完成请求

转载 作者:搜寻专家 更新时间:2023-11-01 07:17:03 26 4
gpt4 key购买 nike

我想实现的目标:

Swift 3.0 中,我目前正在尝试生成一个大型 XML 文件,我想通过 HTTP POST 请求将其直接发送到网络服务器。因为这个 XML 文件可能会变得非常大,所以我不想将它完全存储在内存中,或者先将其写入磁盘,然后在发送到服务器时再逐行读取。

我已经实现了生成 XML 文件的类,它可以写入 OutputStream。这样,无论该流指向磁盘上的文件、内存中的 Data 对象,还是(希望)HTTP POST 请求的主体,都无关紧要。

我打算做什么:

在为 URLSessionStream 类及其同伙搜索(有点稀缺)Swift 文档后,我决定使用 URLSession.uploadTask(withStreamedRequest) 任务。此请求需要通过委托(delegate)方法之一传递 InputStream:

urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)

在此回调中,我使用 Stream.getBoundStreams() 绑定(bind)了一个 InputStreamOutputStream,之后我传递了 OutputStream 到生成 XML 的类,并从委托(delegate)方法返回 InputStream。因此,委托(delegate)方法如下所示:

func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
//Create the input and output stream and bind them, so that what the
//output stream writes ends up in the buffer of the input stream.
var input: InputStream? = nil
var output: OutputStream? = nil

let bufferSize: Int = 1024
Stream.getBoundStreams(withBufferSize: bufferSize, inputStream: &input, outputStream: &output)

//This part is not really important for you, it starts the generation of
//the XML, which is written directly to the output stream.
let converter = DatabaseConverterXml(prettyPrint: false)
let type = ConverterTypeSynchronization(progressAlert: nil)

type.convert(using: converter, writingTo: [Writable.Stream(output!)])
{
successfull in
print("Conversion Complete! Successfull: \(successfull)" )
}

//The input stream is then handed over via the
//completion handler of the delegate method.
completionHandler(input!)
}

我遇到的问题:

有时,生成 XML 的类在将下一行写入 OutputStream 之前可能需要一些时间。如果这种情况发生的时间过长,InputStream 可能会读取太多内容,以至于它实际上会清除整个缓冲区。当这种情况发生时,URLSession 框架(或者可能是 URLSessionUploadTask 本身)认为请求现在已完成并“提交”或“完成”它。然而,这是一个猜测,因为我不确定这些类的内部工作原理(而且文档似乎对我帮助不大)。这会导致我的网络服务器收到不完整的 XML 文件并返回 500 Internal Server Error

我的问题:

有什么方法可以阻止请求提前完成?最好是,我想在 type.convert 调用的回调中“完成”输入流,因为我确信此时不会发生更多写入(并且 OutputStream 实际上是关闭的)。

奖励积分:

这是解决我要解决的问题的正确方法吗?有没有什么办法可以直接与写入 HTTP 正文的流进行交互?我对这个 URLSession 框架感到很迷茫,我花了一天半的时间才走到这一步,所以非常感谢任何建议。我会请任何能帮我解决这个问题的人喝一两杯啤酒!

在此先感谢您的帮助!

编辑 1:

正如@dgatwood 所指出的,一些变量没有正确保留。我已进行以下更改以确保它们确实如此:

var mInput: InputStream? = nil
var mOutput: OutputStream? = nil
var mConverter: DatabaseConverterXml? = nil
var mType: ConverterTypeSynchronization? = nil

func urlSession(_ session: URLSession, task: URLSessionTask, needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
{
//Create the input and output stream and bind them, so that what the
//output stream writes ends up in the buffer of the input stream.
let bufferSize: Int = 1024
Stream.getBoundStreams(withBufferSize: bufferSize, inputStream: &mInput, outputStream: &mOutput)

//This part is not really important for you, it starts the generation of
//the XML, which is written directly to the output stream.
mConverter = DatabaseConverterXml(prettyPrint: false)
mType = ConverterTypeSynchronization(progressAlert: nil)

mType.convert(using: mConverter, writingTo: [Writable.Stream(mOutput!)])
{
successfull in
print("Conversion Complete! Successfull: \(successfull)" )
}

//The input stream is then handed over via the
//completion handler of the delegate method.
completionHandler(mInput!)
}

最佳答案

在聊天中进行一些跟进后的简短回答:

  • 执行写入的对象未被保留,因此当它被释放时,它会释放流,流将关闭。
  • 执行写入的对象,即使它正确地检查了 hasSpaceAvailable,也没有检测到短写入(因为可用空间少于正在写入的对象),因此数据在每次写入调用时都会丢失。
  • 执行写入的对象没有在最后关闭流。

顺便说一句,这些几乎是人们在使用基于流的网络 API 时常犯的错误。在使用相关的基础级套接字 API 时,我自己也犯过类似的错误。

IMO,如果 API 只缓冲一个对象而不考虑其长度,然后在套接字缓冲区中仍有空间,则发送可用空间消息会更有意义。这不需要对现有客户端进行任何更改,并且会导致更少的麻烦……但我离题了。

关于Swift - URLSessionUploadTask 在将整个流写入请求正文之前完成请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41580500/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com