Skip to content
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

[BUG] 上传群文件不支持中文文件名 #72

Closed
Natsukage opened this issue Mar 14, 2023 · 0 comments
Closed

[BUG] 上传群文件不支持中文文件名 #72

Natsukage opened this issue Mar 14, 2023 · 0 comments
Labels
bug Something isn't working

Comments

@Natsukage
Copy link
Contributor

Natsukage commented Mar 14, 2023

发生了什么事?
使用FileManager.UploadFileAsync()可以正常上传英文数字文件名的文件,但是当文件名包含中文日文等字符时MAH就会反馈500错误,上传失败。提示信息类似于

2023-03-14 08:57:21 E/MAH Access: java.lang.IllegalArgumentException: Chars ':*?"<>|' are not allowed in path. RemoteFile path contains illegal char: '?'. path='=?utf-8?B?5rWLMTg4NDY1OTguemlw?='
java.lang.IllegalArgumentException: Chars ':*?"<>|' are not allowed in path. RemoteFile path contains illegal char: '?'. path='=?utf-8?B?5rWLMTg4NDY1OTguemlw?='
        at net.mamoe.mirai.internal.utils.FileSystem.checkLegitimacy(FileSystem.kt:17)
        at net.mamoe.mirai.internal.utils.FileSystem.normalize(FileSystem.kt:26)
        at net.mamoe.mirai.internal.contact.file.RemoteFilesImpl$Companion.findFileByPath(RemoteFilesImpl.kt:34)
        at net.mamoe.mirai.internal.contact.file.CommonAbsoluteFolderImpl.uploadNewFile$suspendImpl(AbsoluteFolderImpl.kt:400)
        at net.mamoe.mirai.internal.contact.file.CommonAbsoluteFolderImpl.uploadNewFile(AbsoluteFolderImpl.kt)
        at net.mamoe.mirai.contact.file.AbsoluteFolder.uploadNewFile$default(AbsoluteFolder.kt:194)
        at mirai-api-http-2.9.1.mirai2.jar//net.mamoe.mirai.api.http.adapter.internal.action.FileKt.onUploadFile(file.kt:62)
        at mirai-api-http-2.9.1.mirai2.jar//net.mamoe.mirai.api.http.adapter.http.router.FileKt$fileRouter$1$invoke$$inlined$httpAuthedMultiPart$1$1.invokeSuspend(dsl.kt:228)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

怎么复现?
直接使用FileManager.UploadFileAsync()上传文件名包含中文的文件就可以复现,除了http返回外,Mirai窗口也会有错误提示。

截图
TMFJ(210R)IW4ARQYH5R{T

环境

  • Mirai版本: 2.14.0
  • MAH版本: 2.9.1
  • Mirai.Net版本: 2.4.8

补充信息
在MAH处提出了issue #698
似乎这是.NET Standard 2.0的MultipartFormDataContent方法本身有问题,而使用了它的FlUrl等也自然有了同样的问题。
.net 6.0的MultipartFormDataContent将file=项进行了错误的编码,而MAH并不支持这样的编码。
例如当编码新建文本文档.txt时,MultipartFormDataContent会将filename项编码为filename="=?utf-8?B?5paw5bu65paH5pys5paH5qGjLnR4dA==?="。根据上面issue中其他人的解答,这部分编码的方式是错误的。MAH接收到这串字符串后,会直接将其作为文件名,导致出错。正常情况下,应该直接发送UTF-8编码的filename="新建文本文档.txt"

此问题目前暂且只能通过手搓轮子来解决。希望框架能原生支持。

var hackedFileName = new string(Encoding.UTF8.GetBytes(fileName).Select(b => (char)b).ToArray());
streamContent.Headers.Add("Content-Disposition", $@"form-data; name=file; filename=""{hackedFileName}""; filename*=""{hackedFileName}""");
content.Add(streamContent);

通过以上方式而非FlUrl的.AddFile()方法添加文件

我目前搓的轮子完整代码如下,供参考

public static async Task<Mirai.Net.Data.Shared.File> UploadFile(
            string groupId,
            string filePath,
            string uploadPath = "", string fileName = null)
{
    string url = string.Format("http://{0}/file/upload", MiraiBot.Instance.Address.HttpAddress);
    fileName ??= Path.GetFileName(filePath);
    using var httpClient = new HttpClient();
    using var content = new MultipartFormDataContent
    {
        { new StringContent(MiraiBot.Instance.HttpSessionKey), "sessionKey" },
        { new StringContent("group"), "type" },
        { new StringContent(groupId), "target" },
        { new StringContent(uploadPath), "path" }
    };

    // 从文件加载数据
    var streamContent = new StreamContent(File.Open(filePath, FileMode.Open));

    var hackedFileName = new string(Encoding.UTF8.GetBytes(fileName).Select(b => (char)b).ToArray());
    streamContent.Headers.Add("Content-Disposition", $@"form-data; name=file; filename=""{hackedFileName}""; filename*=""{hackedFileName}""");
    content.Add(streamContent);
    
    // 发送POST请求

    var responseBody = await (await httpClient.PostAsync(url, content)).Content.ReadAsStringAsync();
    //Console.WriteLine(responseBody);
    JObject re = responseBody.ToJObject();
    Mirai.Net.Data.Shared.File file = !re.ContainsKey("name") ? null : re.ToObject<Mirai.Net.Data.Shared.File>();
    
    return file;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant