Article

Uploading Streams of Data データのストリームをアップロードする

Send a stream of data to a server. データのストリームをサーバーに送信します。

Overview 概要

Streaming media apps and long-running apps that send continual updates use an ongoing stream to upload data, rather than sending a single block of data or a flat file. You can configure an instance of URLSessionUploadTask (a subclass of URLSessionTask) to work with a stream that you provide, and then fill this stream with data indefinitely. ストリーミングメディアアプリと連続的なアップデートを送信する長く動作するアプリは、ある継続中のストリームを使ってデータをアップロードします、単一のブロックまたはフラットファイルを送信するのではなく。あなたは、URLSessionUploadTaskURLSessionTaskのサブクラス)のインスタンスを構成設定して、あなたが提供するストリームを扱います、そしてそれからこのストリームをデータで無期限に満たします。

The task gets the stream by calling your session’s delegate, so you need to create a session and set your own code as its delegate. タスクは、ストリームをあなたのセッションのもつ委任先を呼び出すことによって取得します、なのであなたはあるセッションを作成して、それの委任先としてあなた独自のコードを設定する必要があります。

Create a URL Session URLセッションを作成する

Begin by creating a URLSession and providing it with a delegate. Listing 1 creates a URL session with the default URLSessionConfiguration and sets self as the delegate. You’ll implement URLSessionTaskDelegate later, in Provide the Stream to the Upload Task. URLSessionを作成してそれにある委任先を提供することによって始めます。コード出力 1 は、あるURLセッションを省略時のURLSessionConfigurationで作成して、selfを委任先として設定します。あなたは、URLSessionTaskDelegateを後で実装します、ストリームをアップロードタスクに提供するにおいて。

Listing 1 Creating a URLSession with a delegate コード出力 1 URLSessionをある委任先をつかって作成する

lazy var session: URLSession = URLSession(configuration: .default,
                                          delegate: self,
                                          delegateQueue: .main)

Create a Streaming Upload Task ストリーミングアップロードタスクを作成する

Create the upload task with the URLSession method uploadTask(withStreamedRequest:). This takes a URLRequest specifying the URL you want to upload to, along with other parameters. You start the task by calling resume(). Listing 2 shows how to create and start an upload task, connecting to a server on the local machine (127.0.0.1) listening on port 12345. アップロードタスクをURLSessionのメソッドuploadTask(withStreamedRequest:)で作成してください。これは、あなたがそれへとアップロードしたいURLを指定しているURLRequestを、他のパラメータと一緒に取ります。あなたは、タスクをresume()を呼び出すことによって始めます。コード出力 2 は、ローカルマシン(127.0.0.1)上のサーバーに接続して、ポート12345で聞き耳を立てている、あるアップロードタスクをどのように作成して開始するかを示します。

Listing 2 Creating an upload task コード出力 2 アップロードタスクを作成する

let url = URL(string: "http://127.0.0.1:12345")!
var request = URLRequest(url: url,
                         cachePolicy: .reloadIgnoringLocalCacheData,
                         timeoutInterval: 10)
request.httpMethod = "POST"
let uploadTask = session.uploadTask(withStreamedRequest: request)
uploadTask.resume()

Use a Bound Pair of Streams to Provide an Input Stream ストリームの束縛対を使って入力ストリームを提供する

You provide the streaming data to the upload task as an InputStream. The task reads data from this stream and uploads it to the destination. あなたは、ストリーミングデータをアップロードタスクにInputStreamとして提供します。タスクは、データをこのストリームから読み出してそれを行き先にアップロードします。

A good way to provide data to the input stream is to use a bound pair of streams. The bound pair contains an OutputStream that you write data to. Thanks to the binding of the streams, the data you write to the output stream is made available to the input stream, which the task can then read from. Figure 1 shows this arrangement. データを入力ストリームに提供する1つの良い方法は、ストリームの束縛対を使うことです。束縛対は、あなたがデータをそれへと書き出すOutputStreamを含んでいます。ストリームそれらの束縛に感謝しましょう、あなたが出力ストリームへと書き出すデータは、入力ストリームに利用できるようにされます、それはタスクがその時それから読み出せます。図 1 は、この取り決めを示します。

Figure 1 Providing data to an upload task with a bound pair of streams 図 1 データをアップロードタスクにストリームの束縛対で提供する
Flow diagram showing how data written by an app to the output stream of a bound pair goes into a buffer, then to the bound pair's input stream, then to the upload task, which sends it to the destination.

Listing 3 shows a structure called Streams that consists of an InputStream and an OutputStream. The listing creates a property of this type, called boundStreams, by calling the getBoundStreams(withBufferSize:inputStream:outputStream:) method of the Stream class, passing in in-out references for the input and output streams. コード出力 3 は、Streamsと呼ばれる構造体を示します、それはInputStreamOutputStreamから成ります。コード出力は、この型のboundStreamsと呼ばれるあるプロパティを、StreamクラスのgetBoundStreams(withBufferSize:inputStream:outputStream:)メソッドを、入力および出力ストリームに対するin-out参照を渡して、呼び出すことによって作成します。

Listing 3 Creating a bound pair of input and output streams コード出力 3 入力および出力ストリームの束縛対を作成する

struct Streams {
    let input: InputStream
    let output: OutputStream
}
lazy var boundStreams: Streams = {
    var inputOrNil: InputStream? = nil
    var outputOrNil: OutputStream? = nil
    Stream.getBoundStreams(withBufferSize: 4096,
                           inputStream: &inputOrNil,
                           outputStream: &outputOrNil)
    guard let input = inputOrNil, let output = outputOrNil else {
        fatalError("On return of `getBoundStreams`, both `inputStream` and `outputStream` will contain non-nil streams.")
    }
    // configure and open output stream
    output.delegate = self
    output.schedule(in: .current, forMode: .default)
    output.open()
    return Streams(input: input, output: output)
}()

When you create the bound pair, make sure you specify a buffer size large enough to hold any data you write to the output stream, prior to the data being read from the input stream. Listing 3 uses a 4096-byte buffer. あなたが束縛対を作成する場合、入力ストリームから読み出されるデータに先だって、あなたが出力ストリームに書き出す何らかのデータを保持するのに十分に大きいバッファサイズを指定することを確実にしてください。コード出力 3 は、4096バイトのバッファを使います。

The listing also sets self as the output stream’s delegate. Declare that your class implements the StreamDelegate protocol in order to receive events that indicate when the output stream is ready to receive new data. You’ll provide the implementation of StreamDelegate later, in Write Data to the Stream When It’s Ready. コード出力はまた、selfを出力ストリームのもつ委任先として設定します。あなたのクラスがStreamDelegateプロトコルを、いつ出力ストリームが新しいデータを受け取る準備が出来るかを指し示すイベントを受け取る手段として実装することを宣言してください。あなたは、StreamDelegateの実装を後で提供します、データをストリームにそれが準備できる時に書き出すにおいて。

Provide the Stream to the Upload Task ストリームをアップロードタスクに提供する

You provide the input stream to the upload task in your implementation of the URLSessionTaskDelegate method urlSession(_:task:needNewBodyStream:), which is called after you start the upload task by calling resume(). The callback passes in a completion handler, which you call directly, passing in the boundStreams.input stream you created earlier. Listing 4 shows an implementation of this method. あなたは、入力ストリームをアップロードタスクへと、URLSessionTaskDelegateのメソッドurlSession(_:task:needNewBodyStream:)のあなたの実装において提供します、それは後であなたがアップロードタスクをresume()を呼び出すことによって開始する後に呼び出されます。このコールバックは完了ハンドラに渡されます、それはあなたが直接に呼び出します、あなたが前に作成したboundStreams.inputストリームを渡して。コード出力 4 は、このメソッドの実装を示します。

Listing 4 Providing the input stream to the upload task in the delegate callback コード出力 4 入力ストリームをアップロードタスクに委任先コールバックにおいて提供する

func urlSession(_ session: URLSession, task: URLSessionTask,
                needNewBodyStream completionHandler: @escaping (InputStream?) -> Void) {
    completionHandler(boundStreams.input)
}

Write Data to the Stream When It’s Ready データをストリームにそれが準備できる時に書き出す

Write data to an output stream only when the stream is ready for it. You get notified of the stream’s readiness in the StreamDelegate method stream(_:handle:). When this callback sends hasSpaceAvailable as its eventCode parameter, the stream is ready to accept more data. データを出力ストリームに、ストリームがそれに対して準備できている時に限り書き出してください。あなたは、ストリームの準備できていることをStreamDelegateのメソッドstream(_:handle:)において通知されます。このコールバックがhasSpaceAvailableをそれのeventCodeパラメータとして送る場合、ストリームはさらなるデータを受け入れる準備ができています。

If you’re not ready to write while handling the event, and would prefer to write on your own schedule, you can set a flag variable and check it later to determine whether it’s is safe to write to the stream. Listing 5 illustrates this technique. It handles the hasSpaceAvailable event by just setting a private canWrite property to true. あなたが書き出す準備ができていない一方でそのイベントを処理している、そしてあなた独自の予定で書き出すのを望むならば、あなたはあるフラグ変数を設定してそれを後で確認することで、ストリームに書き出すことが安全であるかどうかを決定できます。コード出力 5 は、このテクニックを例示します。それは、hasSpaceAvailableイベントを、単にあるプライベートなcanWriteプロパティを trueに設定することによって処理します。

While handling stream events, also check whether eventCode is errorOccurred. This means that the stream has failed. When this happens, close the streams and abandon the upload. ストリームイベントを処理する一方で、同様にeventCodeerrorOccurredであるかどうか確認します。これは、ストリームが失敗したことを意味します。これが起こる場合、ストリームを閉じてアップロードを放棄してください。

Listing 5 Handling StreamDelegate events コード出力 5 StreamDelegateイベントを処理する

func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
    guard aStream == boundStreams.output else {
        return
    }
    if eventCode.contains(.hasSpaceAvailable) {
        canWrite = true
    }
    if eventCode.contains(.errorOccurred) {
        // Close the streams and alert the user that the upload failed.
    }
}

Once you’re handling the hasSpaceAvailable event, you can write to the stream whenever you know it’s ready to receive more data. You write to the stream by calling its write(_:maxLength:) method, providing a reference to the raw bytes to be written, and the maximum number of bytes to write. 一旦あなたがhasSpaceAvailableイベントを処理するならば、あなたはストリームに書き出せます、それがさらにデータを受け取る準備ができているのをあなたが知る時はいつでも。あなたは、ストリームにそれのwrite(_:maxLength:)メソッドを呼び出すことによって書き出します、書き出されることになる生のバイトへの参照、そして書き出される最大バイト数を提供して。

Listing 6 uses a timer to wait for the private canWrite property to become true. Once this is the case, the code creates a string representing the current date and converts it to raw bytes. The listing then calls write(_:maxLength:) to send these bytes to the output stream. Because this output stream is bound to an input stream, the upload task can then automatically read these bytes from the input stream and send them to the destination URL. コード出力 6 は、あるタイマーを使うことで、プライベートcanWriteプロパティがtrueになるのを待ちます。一旦そうなるならば、コードは現在の日付を表している文字列を作成して、それを生のバイトに変換します。コード出力は、それからwrite(_:maxLength:)を呼び出すことで、それらバイトを出力ストリームに送ります。この出力ストリームがある入力ストリームに束縛されることから、アップロードタスクはそのとき自動的にそれらバイトを入力ストリームから読み出せます、そしてそれらを行き先URLに送ります。

Listing 6 Creating a timer to write to the output stream when the stream has space available コード出力 6 あるタイマーを作成することで出力ストリームにそのストリームが利用可能な空間を持つ場合に書き出す

timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {
    [weak self] timer in
    guard let self = self else { return }


    if self.canWrite {
        let message = "*** \(Date())\r\n"
        guard let messageData = message.data(using: .utf8) else { return }
        let messageCount = messageData.count
        let bytesWritten: Int = messageData.withUnsafeBytes() { (buffer: UnsafePointer<UInt8>) in
            self.canWrite = false
            return self.boundStreams.output.write(buffer, maxLength: messageCount)
        }
        if bytesWritten < messageCount {
            // Handle writing less data than expected.
        }
    }
}

Once you write to the output stream, you can’t write again until your StreamDelegate receives a new hasSpaceAvailable event. This example enforces this constraint by setting the class’ canWrite property to false. It will be reset to true when a new hasSpaceAvailable event is received by the output stream's delegate, as shown earlier in Listing 5. 一旦あなたが出力ストリームに書き出すならば、あなたは再び書き出すことはできません、あなたのStreamDelegateが新しいhasSpaceAvailableイベントを受け取るまでは。この例は、この制約をそのクラスのもつcanWriteプロパティをfalseに設定することによって強制します。それはtrueに再設定されるでしょう、新しいhasSpaceAvailableイベントが出力ストリームのもつ委任先によって受け取られる時に、前のコード出力 5で示されるように。

See Also 参照

Uploading アップロード