Upload files

Here you can find all the detailed information and API specification for uploading files to a transfer.

Files can be added to a transfer after it has been initialized with Initialize. It is also possible to add files to existing transfers after they are completed (after calling Complete). Follow these guidelines to send files effecitvley:

  • Use POST method.

  • Use Content-Type: application/octet-stream request header and send binary file data directly in the POST body. E.g. in .NET use ByteArrayContent together with PostAsync method of a HttpClient.

  • Specify upload parameters in the request query string. Please note that the request body is reserved for file binary data.

  • File upload URL is returned in the data.transferurl field of the JSON response after calling Initialize. Expect a value similar to https://8888.filemail.com/savefile.ashx .

  • In case the server returns HTTP status 406 or 449 during file upload - retry the request.

  • If you are uploading lots of smaller files you will benefit from uploading a few files in parallel. Keep the number of parallel files on a reasonably low level (2 ... 4) in order to prevent bandwidth saturation.

Chunking

When sending files larger than 50MB it is strongly recommended to perform upload in chunks. Send each chunk in a separate HTTPS request. It is not guaranteed that Filemail Fileservers will accept requests larger than 50MB.

Key points around chunking:

  • Chunking allows to keep HTTP requests small and in turn makes retrying failed chunks easy. In case of a non-chunked upload a network failure at e.g. 90% of a 1GB file makes the retry-logic very inefficient.

  • Pick a chunk size in the range of 5...50MB and add the number of bytes as query string parameter e.g. for a 5MB chunk use ...&chunksize=5000000&.... Note: this value should be constant for all chunks for a given file, even for the last chunk of a file (which is usually smaller than all the other chunks).

  • Calculate total number of chunks as Math.Ceiling(FileSize/ChunkSize) and add it to every chunk request e.g. ...&chunks=13&...

  • Note: when uploading an empty (zero-byte) file - specify chunks=1.

  • For every chunk specify chunk parameter - this is the 0-based chunk index.

  • It is possible to upload multiple chunks in parallel - this may improve upload overall performance. We recommend to upload at most 4 chunks in parallel.

Query string parameters:

  • transferid - Text - retrieved from Initialize -> data.transferid

  • transferkey - Text - retrieved from Initialize-> data.transferkey

  • thefilename - Text - name of the file as it will appear in the transfer. Make sure this value is properly URL-encoded.

  • chunksize - Number - The expected number of bytes in every chunk. This value must be constant for all chunks of a given file - even for the last chunk. When a file contains 2 chunks, first chunk is 1MB and the second is 700kB - in the second chunk request specify the same chunksize as in the firsrt one (i.e. chunksize=1000000). Required when using chunking, otherwise optional.

  • chunks - Number - The total number of chunks in the file. When file size is 0 bytes this value should be set to 1. Required when using chunking, otherwise optional.

  • chunk - Number - Zero-based index of the current chunk being uploaded. Required when using chunking, otherwise optional.

  • md5 - Text - Base64-encoded MD5 hash of bytes sent in current HTTP request. When using chunking calculate this value for every chunk. E.g. MmdYzU+gCb+g/PqavfGttA==. If the calculated hash on our server is different, then HTTP Status 449 is returned - meaning that the chunk must be uploaded again. Optional.

  • compressed - Bool - Set to true if the data being sent is a compressed (zipped) stream. If this parameter is true our servers will unzip file contents on the fly before they are stored on our servers. Optional.

  • retry - Number - Zero-based index of the current retry attempt (if retries are in use). This value is used only for tracking/logging purposes. Optional.

Example Request (without body)

POST https://8888.filemail.com/savefile.ashx?
    transferid=JIRPAXTDQMVAJZB&
    transferkey=5812ea4388e24035abe5ba7cb06b3b47&
    thefilename=big%20file.jpg&
    chunksize=10000000&
    chunks=13&
    chunk=7&
    retry=2

Host: 8888.filemail.com
Content-Type: application/octet-stream

Example Response

HTTP 200 OK (empty response body)

.NET code snippet - chunked upload

Below code illustrates how you can upload a single file with chunking, but without any parallelism. Here we use 5 MB chunks.

// WARNING: example code, not production-ready
public async Task UploadFile(string filePath)
{
    /// ----- values obtained from /transfer/initialize
    var transferid = "...";
    var transferkey = "...";
    var transferurl = "...";
    /// -----

    const int chunkSize = 5000000;
    var chunk = -1;
    var fi = new FileInfo(filePath);
    var chunks = (int)Math.Ceiling((double)fi.Length / chunkSize);
    var query = System.Web.HttpUtility.ParseQueryString(string.Empty);
    query["transferid"] = transferid;
    query["transferkey"] = transferkey;
    query["thefilename"] = fi.Name;
    query["chunks"] = chunks.ToString();
    query["chunksize"] = chunkSize.ToString();

    var buffer = new byte[chunkSize];
    var httpClient = new HttpClient();
    var uriBuilder = new UriBuilder(transferurl);

    using (var fs = fi.OpenRead())
    {
        int read;
        while ((read = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
        {
            chunk += 1;
            query["chunk"] = chunk.ToString();

            uriBuilder.Query = query.ToString();
            var request = new HttpRequestMessage(HttpMethod.Post, uriBuilder.ToString());
            request.Content = new ByteArrayContent(buffer, 0, read);
            request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(System.Net.Mime.MediaTypeNames.Application.Octet);

            var response = await httpClient.SendAsync(request);
            // a single chunk is uploaded now
            // TODO: do something with the chunk response
        }
    }

    // entire file is uploaded now - move on to next file in the transfer
}

The above code loads every chunk into memory (see the buffer variable and fs.ReadAsync method call). If you absolutely need to minimize memory pressure you can upload entire file using streaming. This might not work for large files though. Check out .NETs StreamContent:

var request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content = new StreamContent(fs);

Last updated

Was this helpful?