Skip to content

Commit

Permalink
Address non-ASCII filename support for files, audio, etc. (#75)
Browse files Browse the repository at this point in the history
* allow filename* use in multipart/form-data

* remove unwarranted breaking change description

* minor: align filename->fileName
  • Loading branch information
trrwilson committed Jun 19, 2024
1 parent aca1900 commit 2ba8e69
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 45 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Release History

## 2.0.0-beta.6 (Unreleased)

## Bugs Fixed

- ([#72](https://github.com/openai/openai-dotnet/issues/72)) Fixed `filename` request encoding in operations using `multipart/form-data`, including `files` and `audio`

## 2.0.0-beta.5 (2024-06-14)

## Features Added
Expand Down
60 changes: 15 additions & 45 deletions src/Utility/MultipartFormDataBinaryContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@ public string ContentType

internal HttpContent HttpContent => _multipartContent;

public void Add(Stream content, string name, string fileName = default, string contentType = null)
public void Add(Stream stream, string name, string fileName = default, string contentType = null)
{
Argument.AssertNotNull(content, nameof(content));
Argument.AssertNotNullOrEmpty(name, nameof(name));
Argument.AssertNotNull(stream, nameof(stream));

Add(new StreamContent(content), name, fileName, contentType);
StreamContent content = new(stream);
if (contentType is not null)
{
content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
}
Add(content, name, fileName);
}

//public void Add(Stream stream, string name, string fileName = default)
//{
// Add(new StreamContent(stream), name, fileName);
//}

public void Add(string content, string name, string fileName = default)
{
Add(new StringContent(content), name, fileName);
Expand Down Expand Up @@ -76,48 +75,19 @@ public void Add(BinaryData content, string name, string fileName = default)
Add(new ByteArrayContent(content.ToArray()), name, fileName);
}

private void Add(HttpContent content, string name, string filename, string contentType)
{
if (filename != null)
{
Argument.AssertNotNullOrEmpty(filename, nameof(filename));
AddFileNameHeader(content, name, filename);
}
if (contentType != null)
{
Argument.AssertNotNullOrEmpty(contentType, nameof(contentType));
AddContentTypeHeader(content, contentType);
}
_multipartContent.Add(content, name);
}

private void Add(HttpContent content, string name, string fileName)
{
Argument.AssertNotNull(content, nameof(content));
Argument.AssertNotNull(name, nameof(name));

if (fileName is not null)
{
AddFileNameHeader(content, name, fileName);
_multipartContent.Add(content, name, fileName);
}

_multipartContent.Add(content, name);
}

private static void AddFileNameHeader(HttpContent content, string name, string filename)
{
// Add the content header manually because the default implementation
// adds a `filename*` parameter to the header, which RFC 7578 says not
// to do. We are following up with the BCL team per correctness.
ContentDispositionHeaderValue header = new("form-data")
else
{
Name = name,
FileName = filename
};
content.Headers.ContentDisposition = header;
}

public static void AddContentTypeHeader(HttpContent content, string contentType)
{
MediaTypeHeaderValue header = new MediaTypeHeaderValue(contentType);
content.Headers.ContentType = header;
_multipartContent.Add(content, name);
}
}

#if NET6_0_OR_GREATER
Expand Down
12 changes: 12 additions & 0 deletions tests/Files/FileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,17 @@ public void SerializeFileCollection()
// TODO: Add this test.
}

[Test]
public async Task NonAsciiFilename()
{
FileClient client = GetTestClient();
string filename = "你好.txt";
BinaryData fileContent = BinaryData.FromString("世界您好!这是个测试。");
OpenAIFileInfo uploadedFile = IsAsync
? await client.UploadFileAsync(fileContent, filename, FileUploadPurpose.Assistants)
: client.UploadFile(fileContent, filename, FileUploadPurpose.Assistants);
Assert.That(uploadedFile?.Filename, Is.EqualTo(filename));
}

private static FileClient GetTestClient() => GetTestClient<FileClient>(TestScenario.Files);
}

0 comments on commit 2ba8e69

Please sign in to comment.