MinIO文件上传

image、audio、video

920 发布: 2025/8/3 08:06 本文总阅读量

文档

MinIO

流程

  • Node环境下生成上传地址
  • 客户端使用文件上传的方式向第一步生成的地址PUT方式上传文件

使用方式

  • 需要先由服务单生成上传地址
[RKMediaUploader uploadImage:img toURLString:uploadUrl fileName:imgName progress:^(float progress) {
    NSLog(@"minio-image-progress:%f",progress);
    if (ybprogress) {
        ybprogress(progress);
    }
} completion:^(int code, NSString *key, NSString *msg) {
    if (code == 0) {
        NSLog(@"minio-image-:%d-%@-%@",code,key,msg);
        NSString *linkKey = [NSString stringWithFormat:@"minio_%@",key];
        complete(0,linkKey);
    }else {
        if (complete) {
            complete(code,msg);
        }
    }
}];

客户端上传代码

RKMediaUploader.h

//
//  RKMediaUploader.h
//  YBLiveOne
//
//  Created by yunbao02 on 2025/8/2.
//  Copyright © 2025 iOS. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void(^UploadProgressBlock)(float progress);
//typedef void(^UploadCompletionBlock)(id response, NSError *error);
// code=0 成功
typedef void(^UploadCompletionBlock)(int code,NSString *key,NSString *msg);

@interface RKMediaUploader : NSObject

/**
 上传图片

 @param image 要上传的图片
 @param urlString 上传接口URL
 @param fileName 文件名
 @param progress 进度回调
 @param completion 完成回调
 */
+ (void)uploadImage:(UIImage *)image
        toURLString:(NSString *)urlString
           fileName:(NSString *)fileName
           progress:(UploadProgressBlock)progress
         completion:(UploadCompletionBlock)completion;

/**
 上传视频/音频

 @param filePath 视频/音频本地URL
 @param urlString 上传接口URL
 @param fileName 文件名
 @param progress 进度回调
 @param completion 完成回调
 */
+ (void)uploadVideoOrAudioWithFilePath:(NSString *)filePath
                           toURLString:(NSString *)urlString
                              fileName:(NSString *)fileName
                              progress:(UploadProgressBlock)progress
                            completion:(UploadCompletionBlock)completion;

@end

RKMediaUploader.m

//
//  RKMediaUploader.m
//  YBLiveOne
//
//  Created by yunbao02 on 2025/8/2.
//  Copyright © 2025 iOS. All rights reserved.
//

#import "RKMediaUploader.h"

#import "RKMediaUploaderDelegate.h"

@implementation RKMediaUploader

// 上传图片,适配PUT方法
+ (void)uploadImage:(UIImage *)image
        toURLString:(NSString *)urlString
           fileName:(NSString *)fileName
           progress:(UploadProgressBlock)progress
         completion:(UploadCompletionBlock)completion {
    if (!image || !urlString.length || !fileName || !fileName.length) {
        if (completion) {
            // NSError *error = [NSError errorWithDomain:@"RKMediaUploader" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"图片或URL为空"}];
            // completion(nil, error);
            completion(-1,@"",@"图片、上传地址、文件名为空");
        }
        return;
    }

    // 处理图片数据,尝试JPEG和PNG两种格式
    NSData *imageData = UIImagePNGRepresentation(image);
    NSString *mimeType = @"image/png";

    if (!imageData) {
        imageData = UIImageJPEGRepresentation(image, 0.8);
        mimeType = @"image/jpeg";
    }

    // 检查图片数据是否有效
    if (!imageData) {
        if (completion) {
            // NSError *error = [NSError errorWithDomain:@"RKMediaUploader" code:-4 userInfo:@{NSLocalizedDescriptionKey:@"无法将图片转换为数据"}];
            // completion(nil, error);
            completion(-1,@"",@"无法将图片转换为数据");
        }
        return;
    }

    [self uploadFileData:imageData
                fileName:fileName
                mimeType:mimeType
               urlString:urlString
              parameters:nil
                progress:progress
              completion:completion];
}

// 上传视频,适配PUT方法
+ (void)uploadVideoOrAudioWithFilePath:(NSString *)filePath
                           toURLString:(NSString *)urlString
                              fileName:(NSString *)fileName
                              progress:(UploadProgressBlock)progress
                            completion:(UploadCompletionBlock)completion {
    if (!filePath || !filePath.length || !urlString.length || !fileName || !fileName.length) {
        if (completion) {
            // NSError *error = [NSError errorWithDomain:@"RKMediaUploader" code:-1 userInfo:@{NSLocalizedDescriptionKey:@"视频URL或上传URL为空"}];
            // completion(nil, error);
            completion(-1,@"",@"视频/音频文件、上传地址、文件名为空");
        }
        return;
    }

    // 读取视频数据
    // NSData *videoData = [NSData dataWithContentsOfURL:videoURL];
    NSData *videoData = [NSData dataWithContentsOfFile:filePath];
    if (!videoData) {
        if (completion) {
            // NSError *error = [NSError errorWithDomain:@"RKMediaUploader" code:-2 userInfo:@{NSLocalizedDescriptionKey:@"无法读取视频文件"}];
            // completion(nil, error);
            completion(-1,@"",@"无法读取视频/音频文件");
        }
        return;
    }

    // 类型
    NSString *mineType = @"video/mp4";
    if ([filePath hasSuffix:@".m4a"]) {
        mineType = @"audio/x-m4a";
    }else if ([filePath hasSuffix:@".wav"]){
        mineType = @"audio/wav";
    }else if ([filePath hasSuffix:@".mp3"]){
        mineType = @"audio/mpeg";
    }

    [self uploadFileData:videoData
                fileName:fileName
                mimeType:mineType
               urlString:urlString
              parameters:nil
                progress:progress
              completion:completion];
}

// 通用文件上传方法,适配PUT
+ (void)uploadFileData:(NSData *)fileData
              fileName:(NSString *)fileName
              mimeType:(NSString *)mimeType
             urlString:(NSString *)urlString
            parameters:(NSDictionary *)parameters
              progress:(UploadProgressBlock)progress
            completion:(UploadCompletionBlock)completion {
    NSURL *url = [NSURL URLWithString:urlString];
    if (!url) {
        if (completion) {
            // NSError *error = [NSError errorWithDomain:@"RKMediaUploader" code:-3 userInfo:@{NSLocalizedDescriptionKey:@"URL格式不正确"}];
            // completion(nil, error);
            completion(-1,@"",@"上传地址格式不正确");
        }
        return;
    }

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"PUT"; // 使用PUT方法

    // 设置正确的Content-Type,直接使用文件的MIME类型
    [request setValue:mimeType forHTTPHeaderField:@"Content-Type"];

    // 如果需要传递文件名,可以通过自定义头字段
    if (fileName) {
        [request setValue:fileName forHTTPHeaderField:@"X-Filename"];
    }

    // 对于PUT方法,直接使用文件数据作为请求体,不需要multipart包装
    NSData *httpBody = fileData;

    // 创建会话配置
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];

    // 创建代理对象并设置回调
    RKMediaUploaderDelegate *delegate = [[RKMediaUploaderDelegate alloc] init];
    delegate.fileName = fileName;
    delegate.progressBlock = progress;
    delegate.completionBlock = completion;
    delegate.responseData = [NSMutableData data];

    // 创建会话,使用自定义代理
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
                                                          delegate:delegate
                                                     delegateQueue:[NSOperationQueue mainQueue]];

    // 创建上传任务
    NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:httpBody];

    // 强引用代理,防止被提前释放
    delegate.task = uploadTask;

    // 开始任务
    [uploadTask resume];
}

@end

RKMediaUploaderDelegate.h

//
//  RKMediaUploaderDelegate.h
//  YBLiveOne
//
//  Created by yunbao02 on 2025/8/2.
//  Copyright © 2025 iOS. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "RKMediaUploader.h"

@interface RKMediaUploaderDelegate : NSObject <NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
@property(nonatomic,strong)NSString *fileName;
@property (nonatomic, copy) UploadProgressBlock progressBlock;
@property (nonatomic, copy) UploadCompletionBlock completionBlock;
@property (nonatomic, strong) NSMutableData *responseData;
@property (nonatomic, strong) NSURLSessionTask *task;

@end

RKMediaUploaderDelegate.m

//
//  RKMediaUploaderDelegate.m
//  YBLiveOne
//
//  Created by yunbao02 on 2025/8/2.
//  Copyright © 2025 iOS. All rights reserved.
//

#import "RKMediaUploaderDelegate.h"

@implementation RKMediaUploaderDelegate

#pragma mark - NSURLSessionTaskDelegate

// 上传进度回调
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
    if (self.progressBlock && totalBytesExpectedToSend > 0) {
        float progress = (float)totalBytesSent / totalBytesExpectedToSend;
        self.progressBlock(progress);
    }
}

// 任务完成回调
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    if (self.completionBlock) {
        if (error) {
            // self.completionBlock(nil, error);
            self.completionBlock(-1,@"",[NSString stringWithFormat:@"%@",error]);
        } else if (self.responseData.length > 0) {
            // 尝试解析JSON
            id responseObject = [NSJSONSerialization JSONObjectWithData:self.responseData options:0 error:nil];
            if (!responseObject) {
                // 解析失败则返回字符串
                responseObject = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
            }
            // self.completionBlock(responseObject, nil);
            self.completionBlock(0,_fileName,@"上传成功");
        } else {
            // self.completionBlock(nil, nil);
            self.completionBlock(0,_fileName,@"上传成功");
        }
    }

    // 清理引用
    self.fileName = nil;
    self.task = nil;
    self.progressBlock = nil;
    self.completionBlock = nil;
}

#pragma mark - NSURLSessionDataDelegate

// 接收响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    // 允许处理响应数据
    completionHandler(NSURLSessionResponseAllow);
}

// 接收数据
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [self.responseData appendData:data];
}

@end