Skip to content

Address non-ASCII filename support for files, audio, etc. #75

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
62 changes: 16 additions & 46 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)
private void Add(HttpContent content, string name, string filename)
{
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);
}
Argument.AssertNotNull(content, nameof(content));
Argument.AssertNotNull(name, nameof(name));

private void Add(HttpContent content, string name, string fileName)
{
if (fileName is not null)
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);
}
Loading