Search Results for

    Show / Hide Table of Contents

    Downloading files

    ResumableDownloader.DownloadFileAsync downloads one URL to one file. It works in the Editor and in players.

    How resuming works

    The downloader writes to <target>.downloading. When that file already exists, it asks the server only for the missing bytes, with an HTTP Range request.

    The downloader resumes only when it can prove the bytes on disk belong to the same file. When a download starts, it saves the URL, the server's ETag or Last-Modified value and the file size next to the partial file. A resume needs the same URL and sends that value back (If-Range), so the server returns the whole file when it has changed. The downloader then starts over in the same request.

    It also starts over, at most once, when:

    • the server ignores Range and sends the whole file
    • the server rejects the range (416), for example because the partial file is longer than the remote file
    • the server returns another range or another total size than expected
    • a partial file has no saved server info, for example from another tool
    • a partial file came from another URL
    • the server gave neither a strong ETag nor Last-Modified, so a changed file of the same size could not be detected

    A partial file from the same URL that already has the full size is moved into place without a request.

    Note

    Servers without Range support, or without a strong ETag or Last-Modified, still work, but an interrupted download starts over. Keep the URL stable between calls: a different URL for the same target, such as a signed URL that changes per call, also starts over.

    The target is replaced only when complete

    The target file changes only after a complete download. Before that, the downloader checks the received size against the size the server announced and against expectedSizeInBytes:

    Result Target Partial file
    Complete Replaced Removed
    Too short, for example a dropped connection Unchanged Kept, the next call resumes if the server gave a validator
    Too long, or the server reports a different size than expectedSizeInBytes Unchanged Deleted

    Pass expectedSizeInBytes when you know the size: a wrong size then fails as soon as the response headers arrive, without downloading the body.

    The downloader does not check content hashes. If you have one, check the target file after the download.

    Errors

    Exception When Partial file
    WebRequestException The connection failed or the server returned an error. Url and ResponseCode say which request and why. Kept
    IOException The download is incomplete or has the wrong size. The message names the URL and both sizes. Kept if too short, deleted otherwise
    OperationCanceledException The CancellationToken was cancelled. Kept

    Error responses are never written to the partial file. Bytes received before a dropped connection or a cancellation are kept. To retry, call DownloadFileAsync again.

    Progress

    progress receives a DownloadProgress with DownloadedBytes (including bytes from earlier attempts) and TotalBytes (null when the server does not say). It is reported on the calling thread when the download starts, at most every 100 ms while it runs, and once at the end.

    Cancellation

    Cancel the token to stop a download. The request is aborted and the partial file stays, so a later call resumes.

    Avoid two downloads of the same file

    Two downloads into the same target, in one Editor or in two, must not run at the same time. Hold a FileLock around the download:

    using var fileLock = await FileLock.AcquireAsync(
        targetPath + ".lock",
        TimeSpan.FromSeconds(30),
        cancellationToken);
    await new ResumableDownloader().DownloadFileAsync(url, targetPath, size, progress, cancellationToken);
    

    AcquireAsync waits without blocking the main thread and throws TimeoutException when the lock stays taken. FileLock.TryAcquire takes the lock only if it is free. Disposing the lock releases it.

    Other requests

    UnityWebRequestExtensions.SendAsync awaits any UnityWebRequest, for example a HEAD request for a file size:

    using var request = UnityWebRequest.Head(url);
    await request.SendAsync(cancellationToken);
    var size = request.GetResponseHeader("Content-Length");
    

    It throws WebRequestException for failed requests and OperationCanceledException after cancellation.

    In this article
    Back to top Generated by DocFX