1
This commit is contained in:
@@ -18,11 +18,21 @@
|
||||
|
||||
// 兼容旧命名(如有使用 API_APPLE_LOGIN 的位置,映射到统一命名)
|
||||
#define API_APPLE_LOGIN @"/user/appleLogin" // Apple 登录
|
||||
#define API_EMAIL_LOGIN @"/user/login" // email 登录
|
||||
#define API_LOGOUT @"/user/logout" // 退出登录
|
||||
|
||||
#define API_UPDATA_INFO @"/user/updateInfo" // 更新用户
|
||||
|
||||
#define KB_API_USER_DETAIL @"/user/detail" // 用户详情
|
||||
#define KB_API_CHARACTER_LIST @"/character/list" // 排行榜角色列表(综合)
|
||||
#define KB_API_CHARACTER_LIST_BY_TAG @"/character/listByTag" // 根据 tagId 获取角色列表
|
||||
#define KB_API_TAG_LIST @"/tag/list" // 排行榜标签列表
|
||||
#define KB_API_FILE_UPLOAD @"/file/upload" // 上传头像
|
||||
#define KB_API_CHARACTER_DETAIL @"/character/detail" // 人设详情
|
||||
#define KB_API_CHARACTER_LISTBYUSER @"/character/listByUser" // 人设详情
|
||||
|
||||
|
||||
|
||||
|
||||
// 应用配置
|
||||
#ifndef KB_API_APP_CONFIG
|
||||
|
||||
@@ -6,7 +6,11 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSInteger, UserSex) {
|
||||
UserSexMan = 0, // 男
|
||||
UserSexWeman = 1, // 女
|
||||
UserSexTwoSex = 2, // 两性
|
||||
};
|
||||
@interface KBUser : NSObject
|
||||
|
||||
// 标识
|
||||
@@ -14,12 +18,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// 基本信息
|
||||
@property (nonatomic, copy, nullable) NSString *nickName;
|
||||
@property (nonatomic, copy, nullable) NSString *avatarUrl; // 头像 URL
|
||||
@property (nonatomic, copy, nullable) NSString *gender; // 性别(后端可能返回 string/int,统一转字符串存)
|
||||
/// 0
|
||||
@property (nonatomic, assign) UserSex gender; // 性别(后端可能返回 string/int,统一转字符串存)
|
||||
@property (nonatomic, copy, nullable) NSString *email;
|
||||
/// 邮箱是否验证
|
||||
@property (nonatomic, assign) BOOL emailVerified;
|
||||
|
||||
// 会话信息
|
||||
// token
|
||||
@property (nonatomic, copy, nullable) NSString *token; // token/access_token/accessToken
|
||||
|
||||
@end
|
||||
|
||||
@@ -10,9 +10,26 @@
|
||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||
return @{
|
||||
@"userId": @[@"uid"],
|
||||
@"gender": @[@"gender", @"sex"],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+ (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property {
|
||||
if ([property.name isEqualToString:@"gender"]) {
|
||||
if ([oldValue isKindOfClass:[NSNumber class]]) {
|
||||
NSInteger intValue = [(NSNumber *)oldValue integerValue];
|
||||
if (intValue >= UserSexTwoSex && intValue <= UserSexMan) {
|
||||
return @(intValue);
|
||||
} else {
|
||||
// 如果收到非法值,可以返回默认值
|
||||
KBLOG(@"⚠️ 收到非法的userStatus值: %ld", (long)intValue);
|
||||
return @(UserSexMan);
|
||||
}
|
||||
}
|
||||
return @(UserSexMan);
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -24,6 +24,14 @@ typedef void(^KBLoginCompletion)(BOOL success, NSError * _Nullable error);
|
||||
- (void)signInWithAppleFromViewController:(UIViewController *)presenter
|
||||
completion:(KBLoginCompletion)completion;
|
||||
|
||||
|
||||
/// 邮箱登录
|
||||
- (void)signInWithAppleFromViewController:(UIViewController *)presenter
|
||||
completion:(KBLoginCompletion)completion;
|
||||
|
||||
/// 邮箱登录
|
||||
- (void)emailLoginEmail:(NSString *)email password:(NSString *)password WithCompletion:(KBLoginCompletion)completion;
|
||||
|
||||
/// 是否已登录:由 KBAuthManager 判断(是否存在有效 token)
|
||||
- (BOOL)isLoggedIn;
|
||||
|
||||
|
||||
@@ -70,6 +70,35 @@
|
||||
}];
|
||||
}
|
||||
|
||||
/// 邮箱登录
|
||||
- (void)emailLoginEmail:(NSString *)email password:(NSString *)password WithCompletion:(KBLoginCompletion)completion;
|
||||
{
|
||||
[KBHUD show];
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
if (email.length) params[@"email"] = email;
|
||||
if (password.length) params[@"password"] = password;
|
||||
// 向服务端发起校验
|
||||
[[KBNetworkManager shared] POST:API_EMAIL_LOGIN jsonBody:params headers:nil completion:^(NSDictionary * _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[KBHUD dismiss];
|
||||
if (error) { if (completion) completion(NO, error); return; }
|
||||
NSDictionary *dict = jsonOrData[@"data"];
|
||||
KBUser *user = [KBUser mj_objectWithKeyValues:dict];
|
||||
self.currentUser = user;
|
||||
if (user.token.length == 0) {
|
||||
if (completion) completion(NO, [NSError errorWithDomain:@"KBLogin" code:-2 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"No token returned")}]);
|
||||
return;
|
||||
}
|
||||
[[KBUserSessionManager shared] handleLoginSuccessWithUser:user];
|
||||
completion(true,nil);
|
||||
// 保存登录态到共享钥匙串;供 App 与扩展共享
|
||||
// BOOL ok = [[KBAuthManager shared] saveAccessToken:user.token
|
||||
// refreshToken:nil
|
||||
// expiryDate:nil
|
||||
// userIdentifier:cred.user];
|
||||
// if (completion) completion(ok, ok ? nil : [NSError errorWithDomain:@"KBLogin" code:-3 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Failed to save login state")}]);
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
// 宽松解析:token / access_token / accessToken,支持顶层或 data/user 层
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import "UICollectionViewLeftAlignedLayout.h"
|
||||
#import "KBMyKeyboardCell.h"
|
||||
#import "KBAlert.h"
|
||||
#import "KBMyVM.h"
|
||||
|
||||
/// 复用标识
|
||||
static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
@@ -29,6 +30,7 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
|
||||
// 数据源(必须是二维数组,库内部会在拖动时直接调整顺序)
|
||||
@property (nonatomic, strong) NSMutableArray<NSMutableArray<NSDictionary *> *> *dataSourceArray; // {emoji,title}
|
||||
@property (nonatomic, strong) KBMyVM *viewModel; // 我的页面 VM
|
||||
|
||||
@end
|
||||
|
||||
@@ -40,6 +42,7 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.viewModel = [[KBMyVM alloc] init];
|
||||
self.view.backgroundColor = [UIColor colorWithHex:0xF6F8F9];
|
||||
self.kb_navView.backgroundColor = [UIColor clearColor];
|
||||
self.kb_titleLabel.text = @"My KeyBoard";
|
||||
@@ -71,7 +74,11 @@ static NSString * const kKBMyKeyboardCellId = @"kKBMyKeyboardCellId";
|
||||
}];
|
||||
|
||||
// 初始数据
|
||||
[self buildDefaultData];
|
||||
// [self buildDefaultData];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.viewModel fetchCharacterListByUserWithCompletion:^(NSArray<KBCharacter *> * _Nonnull characterArray, NSError * _Nullable error) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
|
||||
@@ -326,17 +326,26 @@
|
||||
|
||||
- (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results API_AVAILABLE(ios(14.0)) {
|
||||
[picker dismissViewControllerAnimated:YES completion:nil];
|
||||
PHPickerResult *first = results.firstObject; if (!first) return;
|
||||
|
||||
PHPickerResult *first = results.firstObject;
|
||||
if (!first) return;
|
||||
|
||||
NSItemProvider *p = first.itemProvider;
|
||||
if ([p canLoadObjectOfClass:UIImage.class]) {
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[p loadObjectOfClass:UIImage.class completionHandler:^(__kindof id<NSItemProviderReading> _Nullable object, NSError * _Nullable error) {
|
||||
UIImage *img = ([object isKindOfClass:UIImage.class] ? (UIImage *)object : nil);
|
||||
if (!img) return;
|
||||
|
||||
// 这里就在子线程里压缩,避免卡 UI
|
||||
// 比如目标 100KB,你自己改成想要的值
|
||||
NSUInteger targetKB = 50;
|
||||
NSData *compressedData = [weakSelf kb_compressImage:img targetKB:targetKB];
|
||||
if (!compressedData) return;
|
||||
UIImage *compressedImage = [UIImage imageWithData:compressedData];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIImage *compressed = [weakSelf kb_compressImage:img maxPixel:512 quality:0.85];
|
||||
weakSelf.avatarView.image = compressed;
|
||||
weakSelf.avatarJPEGData = UIImageJPEGRepresentation(compressed, 0.85);
|
||||
|
||||
|
||||
});
|
||||
}];
|
||||
}
|
||||
@@ -378,6 +387,95 @@
|
||||
UIImage *result = [UIImage imageWithData:jpeg] ?: scaled ?: image;
|
||||
return result;
|
||||
}
|
||||
/// 按目标大小(KB)压缩图片:
|
||||
/// 1. 先逐步降低 JPEG 质量;
|
||||
/// 2. 如果质量已经降到下限仍然太大,再按比例缩小尺寸,并重复尝试。
|
||||
- (nullable NSData *)kb_compressImage:(UIImage *)image targetKB:(NSUInteger)targetKB {
|
||||
if (!image || targetKB == 0) { return nil; }
|
||||
|
||||
NSUInteger maxBytes = targetKB * 1024;
|
||||
|
||||
// 初始质量参数
|
||||
CGFloat compression = 0.9f;
|
||||
CGFloat minCompression = 0.1f; // 不建议再低了,太低会糊
|
||||
NSData *data = UIImageJPEGRepresentation(image, compression);
|
||||
if (!data) return nil;
|
||||
|
||||
// 如果一开始就小于目标大小,直接返回
|
||||
if (data.length <= maxBytes) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// 1) 先通过降低质量来压缩
|
||||
while (data.length > maxBytes && compression > minCompression + 0.01f) {
|
||||
compression -= 0.1f;
|
||||
data = UIImageJPEGRepresentation(image, compression);
|
||||
if (!data) return nil;
|
||||
}
|
||||
|
||||
if (data.length <= maxBytes) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// 2) 质量降到下限还是太大 -> 等比缩放尺寸
|
||||
UIImage *currentImage = image;
|
||||
|
||||
// 防止死循环,限定最多缩放几次
|
||||
NSInteger maxResizeCount = 6;
|
||||
NSInteger resizeCount = 0;
|
||||
|
||||
while (data.length > maxBytes && resizeCount < maxResizeCount) {
|
||||
resizeCount++;
|
||||
|
||||
// 按面积比例来算缩放因子:新的面积约等于 (maxBytes / 当前字节数) * 原面积
|
||||
CGFloat ratio = (CGFloat)maxBytes / (CGFloat)data.length;
|
||||
// 为了更激进一点,可以乘个经验系数,比如 0.8
|
||||
ratio *= 0.8f;
|
||||
if (ratio <= 0.0f) {
|
||||
ratio = 0.5f; // 兜底,至少缩小一半
|
||||
}
|
||||
|
||||
CGFloat scale = sqrt(ratio);
|
||||
if (scale >= 1.0f) {
|
||||
scale = 0.5f; // 理论上不会走到这里,兜底
|
||||
}
|
||||
|
||||
CGSize newSize = CGSizeMake(currentImage.size.width * scale,
|
||||
currentImage.size.height * scale);
|
||||
if (newSize.width < 10 || newSize.height < 10) {
|
||||
// 太小就没意义了,直接 break,返回目前能做到的最小值
|
||||
break;
|
||||
}
|
||||
|
||||
// 绘制缩小后的图片
|
||||
UIGraphicsBeginImageContextWithOptions(newSize, NO, currentImage.scale);
|
||||
[currentImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
|
||||
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (!resizedImage) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentImage = resizedImage;
|
||||
|
||||
// 每次尺寸缩小之后,再重新从稍高一点的质量开始压一轮
|
||||
compression = 0.9f;
|
||||
data = UIImageJPEGRepresentation(currentImage, compression);
|
||||
if (!data) return nil;
|
||||
|
||||
// 再次在质量范围内往下压
|
||||
while (data.length > maxBytes && compression > minCompression + 0.01f) {
|
||||
compression -= 0.1f;
|
||||
data = UIImageJPEGRepresentation(currentImage, compression);
|
||||
if (!data) return nil;
|
||||
}
|
||||
}
|
||||
|
||||
// 最终返回当前能做到的最小数据(可能略大于目标,但已经尽力)
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
- (KBMyVM *)myVM{
|
||||
if (!_myVM) {
|
||||
|
||||
@@ -6,18 +6,31 @@
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "KBCharacter.h"
|
||||
@class KBUser;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef void(^KBMyUserDetailCompletion)(KBUser *_Nullable user, NSError *_Nullable error);
|
||||
typedef void(^KBCharacterListCompletion)(NSArray<KBCharacter *> *characterArray, NSError *_Nullable error);
|
||||
typedef void(^KBUpLoadAvatarCompletion)(BOOL success, NSError * _Nullable error);
|
||||
typedef void(^KBUpdateUserInfoCompletion)(BOOL success, NSError * _Nullable error);
|
||||
|
||||
@interface KBMyVM : NSObject
|
||||
|
||||
/// 获取当前用户详情(/user/detail)
|
||||
- (void)fetchUserDetailWithCompletion:(KBMyUserDetailCompletion)completion;
|
||||
|
||||
/// 用户人设列表(/character/listByUser)
|
||||
- (void)fetchCharacterListByUserWithCompletion:(KBCharacterListCompletion)completion;
|
||||
|
||||
/// 上传头像
|
||||
- (void)upLoadAvatarWithData:(NSData *)avatarData completion:(KBUpLoadAvatarCompletion)completion;
|
||||
|
||||
/// 更新用户信息
|
||||
- (void)updateUserInfo:(KBUser *)user completion:(KBUpdateUserInfoCompletion)completion;
|
||||
|
||||
|
||||
/// 退出登录
|
||||
- (void)logout;
|
||||
@end
|
||||
|
||||
@@ -42,6 +42,72 @@
|
||||
if (completion) completion(user, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchCharacterListByUserWithCompletion:(KBCharacterListCompletion)completion{
|
||||
[[KBNetworkManager shared] GET:KB_API_CHARACTER_LISTBYUSER
|
||||
parameters:nil
|
||||
headers:nil
|
||||
autoShowBusinessError:NO
|
||||
completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
[KBHUD dismiss];
|
||||
|
||||
if (error) {
|
||||
NSString *msg = KBBizMessageFromJSONObject(jsonOrData) ?: error.localizedDescription ?: KBLocalized(@"Network error");
|
||||
[KBHUD showInfo:msg];
|
||||
if (completion) completion([NSArray new], error);
|
||||
return;
|
||||
}
|
||||
|
||||
id dataObj = jsonOrData[KBData] ?: jsonOrData[@"data"];
|
||||
if (![dataObj isKindOfClass:[NSArray class]]) {
|
||||
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid response")}];
|
||||
[KBHUD showInfo:e.localizedDescription];
|
||||
if (completion) completion([NSArray new], e);
|
||||
return;
|
||||
}
|
||||
NSArray<KBCharacter *> *list = [KBCharacter mj_objectArrayWithKeyValuesArray:(NSArray *)dataObj];
|
||||
if (completion) completion(list, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
/// 上传头像
|
||||
- (void)upLoadAvatarWithData:(NSData *)avatarData completion:(KBUpLoadAvatarCompletion)completion{
|
||||
[KBHUD show];
|
||||
[[KBNetworkManager shared] uploadFile:KB_API_FILE_UPLOAD
|
||||
fileData:avatarData
|
||||
fileName:@"avatar.jpg"
|
||||
mimeType:@"image/jpeg"
|
||||
headers:nil
|
||||
completion:^(NSDictionary * _Nullable json,
|
||||
NSURLResponse * _Nullable response,
|
||||
NSError * _Nullable error) {
|
||||
[KBHUD dismiss];
|
||||
if (error) {
|
||||
NSLog(@"上传失败: %@", error);
|
||||
return;
|
||||
}
|
||||
NSString *avImageString = json[@"data"];
|
||||
// [weakSelf.avatarView kb_setImageURL:[NSURL URLWithString:avImageString] placeholder:KBPlaceholderImage];
|
||||
|
||||
NSLog(@"上传成功: %@", json);
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
/// 更新用户信息
|
||||
- (void)updateUserInfo:(KBUser *)user completion:(KBUpdateUserInfoCompletion)completion{
|
||||
/// 获取用户信息
|
||||
KBUser *localUser = [KBUserSessionManager shared].currentUser;
|
||||
NSMutableDictionary *params = [NSMutableDictionary dictionary];
|
||||
if (localUser.userId.length) params[@"uid"] = localUser.userId;
|
||||
if (localUser.nickName.length) params[@"nickName"] = localUser.nickName;
|
||||
params[@"gender"] = (NSInteger)localUser.gender;
|
||||
if (localUser.avatarUrl.length) params[@"avatarUrl"] = localUser.avatarUrl;
|
||||
|
||||
}
|
||||
|
||||
- (void)logout{
|
||||
[KBHUD show];
|
||||
[[KBNetworkManager shared] GET:API_LOGOUT
|
||||
|
||||
@@ -79,6 +79,28 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
completion:(KBNetworkCompletion)completion;
|
||||
|
||||
/// 上传文件(multipart/form-data,表单字段名固定为 "file")
|
||||
/// path: 例如 @"file/upload" 或 @"/file/upload"
|
||||
/// fileData: 文件二进制数据(比如 UIImageJPEGRepresentation)
|
||||
/// fileName: 例如 @"avatar.jpg"
|
||||
/// mimeType: 例如 @"image/jpeg"
|
||||
- (nullable NSURLSessionDataTask *)uploadFile:(NSString *)path
|
||||
fileData:(NSData *)fileData
|
||||
fileName:(NSString *)fileName
|
||||
mimeType:(NSString *)mimeType
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
autoShowBusinessError:(BOOL)autoShowBusinessError
|
||||
completion:(KBNetworkCompletion)completion;
|
||||
|
||||
/// 便捷版本:默认 autoShowBusinessError = YES
|
||||
- (nullable NSURLSessionDataTask *)uploadFile:(NSString *)path
|
||||
fileData:(NSData *)fileData
|
||||
fileName:(NSString *)fileName
|
||||
mimeType:(NSString *)mimeType
|
||||
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
|
||||
completion:(KBNetworkCompletion)completion;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -186,6 +186,98 @@ autoShowBusinessError:YES
|
||||
return [self startDataTaskWithRequest:req completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark - Upload (multipart/form-data)
|
||||
|
||||
- (NSURLSessionDataTask *)uploadFile:(NSString *)path
|
||||
fileData:(NSData *)fileData
|
||||
fileName:(NSString *)fileName
|
||||
mimeType:(NSString *)mimeType
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
autoShowBusinessError:(BOOL)autoShowBusinessError
|
||||
completion:(KBNetworkCompletion)completion
|
||||
{
|
||||
NSLog(@"[KBNetworkManager] UPLOAD called, enabled=%d, path=%@", self.isEnabled, path);
|
||||
if (![self ensureEnabled:completion]) return nil;
|
||||
|
||||
NSString *urlString = [self buildURLStringWithPath:path];
|
||||
if (!urlString) {
|
||||
[self fail:KBNetworkErrorInvalidURL completion:completion];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (!fileData || fileData.length == 0) {
|
||||
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidResponse
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Empty file data")}];
|
||||
if (completion) completion(nil, nil, e);
|
||||
return nil;
|
||||
}
|
||||
|
||||
AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer];
|
||||
serializer.timeoutInterval = self.timeout;
|
||||
|
||||
NSError *error = nil;
|
||||
NSMutableURLRequest *req =
|
||||
[serializer multipartFormRequestWithMethod:@"POST"
|
||||
URLString:urlString
|
||||
parameters:nil
|
||||
constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
|
||||
|
||||
// Apifox 后端要求:表单字段名为 "file"
|
||||
NSString *fieldName = @"file";
|
||||
NSString *fname = fileName ?: @"file";
|
||||
NSString *type = mimeType ?: @"application/octet-stream";
|
||||
|
||||
[formData appendPartWithFileData:fileData
|
||||
name:fieldName
|
||||
fileName:fname
|
||||
mimeType:type];
|
||||
} error:&error];
|
||||
|
||||
if (error || !req) {
|
||||
if (completion) {
|
||||
completion(nil, nil,
|
||||
error ?: [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorInvalidURL
|
||||
userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid upload request")}]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 不强行指定 Content-Type,留给 AFN 里带 boundary 的那一套
|
||||
[self applyHeaders:headers toMutableRequest:req contentType:nil];
|
||||
|
||||
#if DEBUG
|
||||
KBLOG(@"HTTP UPLOAD (multipart)\nURL: %@\nHeaders: %@\nfileName: %@\nlength: %lu",
|
||||
req.URL.absoluteString,
|
||||
req.allHTTPHeaderFields ?: @{},
|
||||
fileName,
|
||||
(unsigned long)fileData.length);
|
||||
#endif
|
||||
|
||||
// 按 JSON 响应处理(和 POST 一样:解析 {code,message,data,...})
|
||||
return [self startJSONTaskWithRequest:req
|
||||
autoShowBusinessError:autoShowBusinessError
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (NSURLSessionDataTask *)uploadFile:(NSString *)path
|
||||
fileData:(NSData *)fileData
|
||||
fileName:(NSString *)fileName
|
||||
mimeType:(NSString *)mimeType
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
completion:(KBNetworkCompletion)completion
|
||||
{
|
||||
return [self uploadFile:path
|
||||
fileData:fileData
|
||||
fileName:fileName
|
||||
mimeType:mimeType
|
||||
headers:headers
|
||||
autoShowBusinessError:YES
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Core
|
||||
|
||||
- (BOOL)ensureEnabled:(KBNetworkCompletion)completion {
|
||||
|
||||
Reference in New Issue
Block a user