3

For uploading images from a client to the server I use chunking.

Here is the client code:

private async Task UploadPersonImage(int personId, string fileName, CancellationToken cancellationToken)
{
    var stream = Client.UploadPersonImage();
    PersonImageMessage personImageMessage = new PersonImageMessage();
    personImageMessage.PersonId = personId;
    personImageMessage.ImageType = ImageType.Jpg;
    byte[] image = File.ReadAllBytes(fileName);
    int imageOffset = 0;
    byte[] imageChunk = new byte[imageChunkSize];
    while (imageOffset < image.Length && !cancellationToken.IsCancellationRequested)
    {
        int length = Math.Min(imageChunkSize, image.Length - imageOffset);
        Buffer.BlockCopy(image, imageOffset, imageChunk, 0, length);
        imageOffset += length;
        ByteString byteString = ByteString.CopyFrom(imageChunk);
        personImageMessage.ImageChunk = byteString;
        await stream.RequestStream.WriteAsync(personImageMessage).ConfigureAwait(false);
    }
    await stream.RequestStream.CompleteAsync().ConfigureAwait(false);
    if (!cancellationToken.IsCancellationRequested)
    {
        var uploadPersonImageResult = await stream.ResponseAsync.ConfigureAwait(false);
        // Process answer...
    }
}

And this is the server code:

public override async Task<TransferStatusMessage> UploadPersonImage(
    IAsyncStreamReader<PersonImageMessage> requestStream, ServerCallContext context)
{
    TransferStatusMessage transferStatusMessage = new TransferStatusMessage();
    transferStatusMessage.Status = TransferStatus.Success;
    try
    {
        await Task.Run(
            async () =>
            {
                CancellationToken cancellationToken = context.CancellationToken;
                await using (Stream fs = File.OpenWrite(ImageFileName))
                {
                    await foreach (PersonImageMessage personImageMessage in
                        requestStream.ReadAllAsync(cancellationToken).ConfigureAwait(false))
                    {
                        fs.Write(personImageMessage.ImageChunk.ToByteArray());
                    }
                }
            }).ConfigureAwait(false);
    }
    // Is thrown on cancellation -> ignore...
    catch (OperationCanceledException)
    {
        transferStatusMessage.Status = TransferStatus.Cancelled;
    }
    catch (RpcException rpcEx)
    {
        if (rpcEx.StatusCode == StatusCode.Cancelled)
        {
            transferStatusMessage.Status = TransferStatus.Cancelled;
        }
        else
        {
            _logger.LogError($"Exception while processing image file '{ImageFileName}'. Exception: '{requestStream}'");
            transferStatusMessage.Status = TransferStatus.Failure;
        }
    }
    // Delete incomplete file
    if (transferStatusMessage.Status != TransferStatus.Success)
    {
        File.Delete(ImageFileName);
    }
    return transferStatusMessage;
}

Everything works fine. I want to cancel the upload in between sending the chunks. Now, CompleteAsync() is called and the server thinks the data transfer ended successfully. I'm looking for a way to trigger the cancellation in the server (i.e. the CancellationToken in the ServerCallContext) via the client. As a workaround I could add a flag to PersonImageMessage, something like 'upload_cancelled', to tell the server that the transfer is aborted. But there must be a built-in mechanism.

Does somebody know the trick?

1 Answer 1

1

You can pass cancellationToken to your Client stream RPC method. Then the server can receive it as context.cancellationToken.

var stream = Client.UploadPersonImage(cancellationToken: cancellationToken);

If the client cancels, stream.RequestStream.CompleteAsync() should not be called.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.