This commit is contained in:
2026-01-15 18:16:56 +08:00
parent da62d4f411
commit 32c4138ae0
29 changed files with 1523 additions and 95 deletions

View File

@@ -64,6 +64,15 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
completion:(KBNetworkCompletion)completion;
/// POST multipart 上传文件(常用于语音/图片等文件)
- (nullable NSURLSessionDataTask *)uploadFile:(NSString *)path
fileURL:(NSURL *)fileURL
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(nullable NSDictionary *)parameters
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
completion:(KBNetworkCompletion)completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -124,6 +124,84 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
return [self startAFJSONTaskWithRequest:req completion:completion];
}
- (NSURLSessionDataTask *)uploadFile:(NSString *)path
fileURL:(NSURL *)fileURL
name:(NSString *)name
mimeType:(NSString *)mimeType
parameters:(NSDictionary *)parameters
headers:(NSDictionary<NSString *, NSString *> *)headers
completion:(KBNetworkCompletion)completion {
[self getSignWithParare:parameters];
if (![self ensureEnabled:completion]) return nil;
NSString *urlString = [self buildURLStringWithPath:path];
if (!urlString) { [self fail:KBNetworkErrorInvalidURL completion:completion]; return nil; }
if (!fileURL) {
if (completion) completion(nil, nil, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid file")}]);
return nil;
}
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
serializer.timeoutInterval = self.timeout;
NSError *error = nil;
NSMutableURLRequest *req = [serializer multipartFormRequestWithMethod:@"POST"
URLString:urlString
parameters:parameters
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSString *safeName = (name.length > 0) ? name : @"file";
NSString *fileName = fileURL.lastPathComponent ?: @"upload.bin";
NSString *type = (mimeType.length > 0) ? mimeType : @"application/octet-stream";
[formData appendPartWithFileURL:fileURL name:safeName fileName:fileName mimeType:type error:nil];
} error:&error];
if (error || !req) {
if (completion) completion(nil, nil, error ?: [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid URL")}]);
return nil;
}
[self applyHeaders:headers toMutableRequest:req contentType:nil];
self.manager.responseSerializer = [AFHTTPResponseSerializer serializer];
NSURLSessionUploadTask *task = [self.manager uploadTaskWithStreamedRequest:req progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
if (completion) completion(nil, response, error);
return;
}
NSData *data = (NSData *)responseObject;
if (![data isKindOfClass:[NSData class]]) {
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"No data")}]);
return;
}
NSString *ct = nil;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
ct = ((NSHTTPURLResponse *)response).allHeaderFields[@"Content-Type"];
}
BOOL looksJSON = (ct && [[ct lowercaseString] containsString:@"json"]);
if (!looksJSON) {
const unsigned char *bytes = data.bytes;
NSUInteger len = data.length;
for (NSUInteger i = 0; !looksJSON && i < len; i++) {
unsigned char c = bytes[i];
if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue;
looksJSON = (c == '{' || c == '[');
break;
}
}
if (looksJSON) {
NSError *jsonErr = nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
if (jsonErr) {
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Failed to parse JSON")}]);
return;
}
if (![json isKindOfClass:[NSDictionary class]]) {
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]);
return;
}
if (completion) completion((NSDictionary *)json, response, nil);
} else {
if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]);
}
}];
[task resume];
return task;
}
- (NSURLSessionDataTask *)GETData:(NSString *)path
parameters:(NSDictionary *)parameters
headers:(NSDictionary<NSString *,NSString *> *)headers