You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Swift及AZSClient将iOS相册视频上传至Azure Blob Storage

iOS视频上传至Azure Blob Storage(AZSClient实现)

首先得明确:视频文件通常比图片大得多,直接像图片那样取Data上传很容易导致内存飙升甚至崩溃。所以咱们换个更稳妥的思路——先获取视频的本地文件URL,再用分块上传的方式传到Azure Blob,这样既省内存,还支持断点续传。

下面是完整的Swift实现步骤:

1. 获取相册视频的本地URL

从PHAsset获取视频的本地路径,这里用PHAssetResourceManager处理,比直接取Data高效得多:

import Photos

func getVideoLocalURL(from asset: PHAsset, completion: @escaping (URL?) -> Void) {
    guard asset.mediaType == .video else {
        completion(nil)
        return
    }
    
    let resources = PHAssetResource.assetResources(for: asset)
    guard let videoResource = resources.first(where: { $0.type == .video }) else {
        completion(nil)
        return
    }
    
    let tempDir = FileManager.default.temporaryDirectory
    let tempFileURL = tempDir.appendingPathComponent("\(UUID().uuidString).mov")
    
    let options = PHAssetResourceRequestOptions()
    options.isNetworkAccessAllowed = true // 允许从iCloud下载视频
    
    PHAssetResourceManager.default().writeData(for: videoResource, toFile: tempFileURL, options: options) { error in
        if let error = error {
            print("导出视频失败: \(error.localizedDescription)")
            completion(nil)
        } else {
            completion(tempFileURL)
        }
    }
}

2. 初始化Azure Blob相关对象

这一步和图片上传逻辑一致,先拿到AZSCloudBlockBlob实例:

import AzureStorageBlob

func getAzureBlockBlob() -> AZSCloudBlockBlob? {
    // 替换成你的Azure存储账号连接字符串
    guard let storageAccount = AZSCloudStorageAccount(fromConnectionString: "你的Azure存储连接字符串") else {
        print("存储账号初始化失败")
        return nil
    }
    
    let blobClient = storageAccount.getBlobClient()
    // 替换成你的容器名称
    let container = blobClient.containerReference(fromName: "你的容器名称")
    // 替换成Blob中要保存的文件名
    let blobName = "uploaded_video_\(UUID().uuidString).mov"
    return container.blockBlobReference(fromName: blobName)
}

3. 分块上传视频到Azure Blob

分块上传是大文件上传的最佳实践,Azure Blob支持最大100MB的分块,这里咱们用4MB作为分块大小(可按需调整):

func uploadVideoToAzure(blob: AZSCloudBlockBlob, localURL: URL, completion: @escaping (Error?) -> Void) {
    let fileHandle: FileHandle
    do {
        fileHandle = try FileHandle(forReadingFrom: localURL)
    } catch {
        completion(error)
        return
    }
    defer { fileHandle.closeFile() }
    
    let fileSize = try! FileManager.default.attributesOfItem(atPath: localURL.path)[.size] as! UInt64
    let blockSize: UInt64 = 4 * 1024 * 1024 // 4MB分块
    let blockCount = (fileSize + blockSize - 1) / blockSize // 计算总块数
    var blockIDs: [String] = []
    
    // 逐个上传分块
    for blockIndex in 0..<blockCount {
        let offset = blockIndex * blockSize
        let readSize = min(blockSize, fileSize - offset)
        
        fileHandle.seek(toFileOffset: offset)
        let blockData = fileHandle.readData(ofLength: Int(readSize))
        
        // 生成唯一的block ID
        let blockID = "\(UUID().uuidString)".data(using: .utf8)!.base64EncodedString()
        blockIDs.append(blockID)
        
        // 上传单个分块
        blob.createBlock(withId: blockID, from: blockData) { error in
            if let error = error {
                completion(error)
                return
            }
            
            // 所有分块上传完成后,提交块列表
            if blockIndex == blockCount - 1 {
                blob.commitBlockList(blockIDs) { error in
                    completion(error)
                }
            }
        }
    }
}

4. 整合流程调用

把上面的方法串起来,完整调用示例:

// 假设你已经获取到目标PHAsset实例
let targetVideoAsset: PHAsset = ...

getVideoLocalURL(from: targetVideoAsset) { localURL in
    guard let localURL = localURL else {
        print("获取视频本地URL失败")
        return
    }
    
    guard let blob = getAzureBlockBlob() else {
        print("Azure Blob实例初始化失败")
        return
    }
    
    uploadVideoToAzure(blob: blob, localURL: localURL) { error in
        if let error = error {
            print("视频上传失败: \(error.localizedDescription)")
        } else {
            print("视频上传成功!Blob URL: \(blob.absoluteString)")
            // 上传完成后可删除临时文件
            try? FileManager.default.removeItem(at: localURL)
        }
    }
}

注意事项

  • 权限配置:别忘了在Info.plist中添加NSPhotoLibraryUsageDescription(iOS13及以下)或NSPhotoLibraryAddUsageDescription/NSPhotoLibraryLimitedUsageDescription(iOS14+),否则无法访问相册。
  • 分块大小调整:视频特别大时可适当增大分块(最大100MB)减少请求次数;网络不稳定时建议用小分块,降低重试成本。
  • 断点续传:如果需要断点续传,可先调用blob.downloadBlockList()获取已上传的块ID,跳过这些块继续上传未完成部分。
  • 错误处理:示例简化了错误处理,实际开发中要针对网络错误、Azure权限错误、文件读取错误等做细致处理。

内容的提问来源于stack exchange,提问作者Rizwan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:06:58