解决了语音上传到问题,接下来要解决下载播放问题

This commit is contained in:
joywayer
2025-06-15 12:36:47 +08:00
parent bba3ed1cb4
commit c11fc62bf1
513 changed files with 31197 additions and 2969 deletions

View File

@@ -0,0 +1,256 @@
//
// QiniuManager.m
// msext
//
// Created on June 15, 2025.
//
#import "QiniuManager.h"
#import <QiniuSDK.h>
#import "QiniuConfig.h"
#import <objc/runtime.h>
#import <CommonCrypto/CommonHMAC.h>
#import <CommonCrypto/CommonDigest.h>
@implementation QiniuManager
+ (instancetype)sharedManager {
static QiniuManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
[instance setupQiniuSDK];
});
return instance;
}
- (void)setupQiniuSDK {
// Qiniu SDK doesn't require explicit initialization
// The configuration will be applied when creating the QNUploadManager instance
}
- (void)uploadAudioFile:(NSString *)filePath
fileName:(NSString *)fileName
progressHandler:(QiniuProgressHandler)progressHandler
completionHandler:(QiniuUploadCompletionHandler)completionHandler {
//
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
NSError *error = [NSError errorWithDomain:@"QiniuManager" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"上传文件不存在"}];
if (completionHandler) {
completionHandler(nil, error);
}
return;
}
//
QNConfiguration *config = [QNConfiguration build:^(QNConfigurationBuilder *builder) {
builder.zone = [QNFixedZone zone2]; // 使
builder.timeoutInterval = 60; //
}];
//
QNUploadManager *uploadManager = [[QNUploadManager alloc] initWithConfiguration:config];
NSString *token = [self generateUploadToken];
//
NSString *key = [NSString stringWithFormat:@"%@%@", kQiniuRecordingDirectory, fileName];
//
QNUploadOption *option = [[QNUploadOption alloc] initWithMime:@"audio/amr"
progressHandler:^(NSString *key, float percent) {
if (progressHandler) {
progressHandler(percent);
}
} params:nil checkCrc:NO cancellationSignal:nil];
//
[uploadManager putFile:filePath
key:key
token:token
complete:^(QNResponseInfo *info, NSString *key, NSDictionary *resp) {
if (info.statusCode == 200) {
if (completionHandler) {
completionHandler(key, nil);
}
} else {
NSError *error = [NSError errorWithDomain:@"QiniuManager"
code:info.statusCode
userInfo:@{NSLocalizedDescriptionKey: info.error.localizedDescription ?: @"上传失败"}];
if (completionHandler) {
completionHandler(nil, error);
}
}
}
option:option];
}
- (void)downloadAudioFile:(NSString *)key
progressHandler:(QiniuProgressHandler)progressHandler
completionHandler:(QiniuDownloadCompletionHandler)completionHandler {
// URL
NSString *urlString = [self getFileUrlWithKey:key];
NSURL *url = [NSURL URLWithString:urlString];
//
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
if (error) {
if (completionHandler) {
completionHandler(nil, error);
}
return;
}
// URL
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
NSError *downloadError = [NSError errorWithDomain:@"QiniuManager"
code:httpResponse.statusCode
userInfo:@{NSLocalizedDescriptionKey: @"下载失败"}];
if (completionHandler) {
completionHandler(nil, downloadError);
}
return;
}
// URL
NSString *fileName = [key lastPathComponent];
if ([fileName length] == 0) {
fileName = [NSString stringWithFormat:@"qiniu_download_%@", [[NSUUID UUID] UUIDString]];
}
//
NSString *cachesDirectory = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject;
NSString *downloadDirectory = [cachesDirectory stringByAppendingPathComponent:@"QiniuDownloads"];
//
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:downloadDirectory]) {
[fileManager createDirectoryAtPath:downloadDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
//
NSString *destinationPath = [downloadDirectory stringByAppendingPathComponent:fileName];
//
if ([fileManager fileExistsAtPath:destinationPath]) {
[fileManager removeItemAtPath:destinationPath error:nil];
}
NSError *moveError = nil;
[fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:destinationPath] error:&moveError];
if (moveError) {
if (completionHandler) {
completionHandler(nil, moveError);
}
} else {
if (completionHandler) {
completionHandler(destinationPath, nil);
}
}
}];
//
if (progressHandler) {
[downloadTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionNew context:NULL];
objc_setAssociatedObject(downloadTask, "progressHandler", [progressHandler copy], OBJC_ASSOCIATION_COPY);
objc_setAssociatedObject(downloadTask, "totalBytes", @(0), OBJC_ASSOCIATION_RETAIN);
}
//
[downloadTask resume];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionDownloadTask class]]) {
NSURLSessionDownloadTask *task = (NSURLSessionDownloadTask *)object;
if ([keyPath isEqualToString:@"countOfBytesReceived"]) {
NSNumber *totalBytes = objc_getAssociatedObject(task, "totalBytes");
if ([totalBytes longLongValue] == 0 && task.countOfBytesExpectedToReceive > 0) {
objc_setAssociatedObject(task, "totalBytes", @(task.countOfBytesExpectedToReceive), OBJC_ASSOCIATION_RETAIN);
}
float progress = 0;
if (task.countOfBytesExpectedToReceive > 0) {
progress = (float)task.countOfBytesReceived / (float)task.countOfBytesExpectedToReceive;
}
QiniuProgressHandler progressHandler = objc_getAssociatedObject(task, "progressHandler");
if (progressHandler) {
dispatch_async(dispatch_get_main_queue(), ^{
progressHandler(progress);
});
}
}
}
}
- (NSString *)getFileUrlWithKey:(NSString *)key {
return [NSString stringWithFormat:@"%@/%@", kQiniuDomain, key];
}
#pragma mark - Private Methods
- (NSString *)generateUploadToken {
// (putPolicy)
NSMutableDictionary *policy = [NSMutableDictionary dictionary];
// bucketNamebucketName:keyPrefix
NSString *scope = [NSString stringWithFormat:@"%@", kQiniuBucketName];
[policy setObject:scope forKey:@"scope"];
// 1
NSInteger deadline = (NSInteger)[[NSDate dateWithTimeIntervalSinceNow:3600] timeIntervalSince1970];
[policy setObject:@(deadline) forKey:@"deadline"];
// JSON
NSError *error = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:policy options:0 error:&error];
if (error) {
NSLog(@"生成JSON数据失败: %@", error);
return nil;
}
// Base64JSON
NSString *encodedPolicy = [self urlsafeBase64EncodeData:jsonData];
// 使HMAC-SHA1encodedPolicy
NSData *signData = [encodedPolicy dataUsingEncoding:NSUTF8StringEncoding];
NSString *encodedSign = [self hmacSha1:kQiniuSecretKey data:signData];
// - : accessKey:encodedSign:encodedPolicy
NSString *uploadToken = [NSString stringWithFormat:@"%@:%@:%@", kQiniuAccessKey, encodedSign, encodedPolicy];
// NSLog(@"七牛云配置信息 - AccessKey: %@, BucketName: %@, Domain: %@",
// kQiniuAccessKey, kQiniuBucketName, kQiniuDomain);
// NSLog(@"生成的上传凭证: %@", uploadToken);
return uploadToken;
}
#pragma mark - Utility Methods
- (NSString *)urlsafeBase64EncodeData:(NSData *)data {
NSString *base64 = [data base64EncodedStringWithOptions:0];
base64 = [base64 stringByReplacingOccurrencesOfString:@"+" withString:@"-"];
base64 = [base64 stringByReplacingOccurrencesOfString:@"/" withString:@"_"];
return base64;
}
- (NSString *)hmacSha1:(NSString *)key data:(NSData *)data {
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [data bytes];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, [data length], cHMAC);
NSData *hmacData = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
return [self urlsafeBase64EncodeData:hmacData];
}
@end