FluentHttpClient icon indicating copy to clipboard operation
FluentHttpClient copied to clipboard

When using WithBody(p => p.FileUpload(filePath)) the file is blocked for deletion

Open GuilhermeGHN opened this issue 1 year ago • 2 comments

After making a simple request by sending a file, it is blocked for manipulation by another process, for example deleting the file itself.

I believe these methods would need some treatment to avoid this problem:

  • HttpContent FileUpload(string fullPath);
  • HttpContent FileUpload(FileInfo file);
  • HttpContent FileUpload(IEnumerable<FileInfo> files);

GuilhermeGHN avatar Aug 07 '24 12:08 GuilhermeGHN

Hi! The file stream is owned by the request message, so we'll need to dispose the request at some point. That would be a breaking change though, since it'll prevent any further access to the request (e.g. when handling the response). I'm not sure off-hand how best to handle that; we'll probably need some API changes.

As a temporary workaround, you can dispose it yourself since you know it won't be accessed later. For example:

IResponse response = null;
try
{
    response = await client
        .PostAsync("api")
        .WithBody(p => p.FileUpload(...))
        .AsResponse();

    return response.As<TModel>();
}
finally
{
    response?.Message.RequestMessage.Dispose();
    response?.Message.Dispose();
}

Pathoschild avatar Aug 07 '24 22:08 Pathoschild

Hello!

I understood the problem, I am already using the following approach:

IResponse response;

using (var fs = File.OpenRead(file))
{
	var files = new List<KeyValuePair<string, Stream>>
	{
		new KeyValuePair<string, Stream>("filename", fs)
	};

	response = await client
		.PostAsync("api")
		.WithBody(p => p.FileUpload(files))
		.AsResponse();

	fs.Flush();
}

Regarding the solution without a break, I took a look at FileUpload and found that the following adjustment might be possible:

private Stream GetFileStream(FileInfo file)
{
	var memoryStream = new MemoryStream();

	using (var fileStream = file.OpenRead())
	{
		fileStream.CopyTo(memoryStream);
	}

	return memoryStream;
}

public HttpContent FileUpload(IEnumerable<FileInfo> files)
{
	return this.FileUpload(
		files.Select(file => file.Exists
			? new KeyValuePair<string, Stream>(file.Name, this.GetFileStream(file))
			: throw new FileNotFoundException($"There's no file matching path '{file.FullName}'.")
		)
	);
}

This way, the methods I mentioned initially would be detached from the files sent as parameters, while the method that receives IEnumerable<KeyValuePair<string, Stream>> files would be left as is, being the responsibility of whoever sent the Streams to manage.

I hope I helped in some way.

GuilhermeGHN avatar Aug 11 '24 22:08 GuilhermeGHN