从远程URL下载视频并将其保存在相册中

问题描述 投票:0回答:5

我当前正在我的应用程序中显示视频,我希望用户能够将其保存到其设备图库/相册照片/相机胶卷中。 这就是我正在做的,但视频没有保存在相册中。

func downloadVideo(videoImageUrl:String)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
        //All stuff here
        
        print("downloadVideo");
        let url=NSURL(string: videoImageUrl);
        let urlData=NSData(contentsOfURL: url!);
        
        if((urlData) != nil)
        {
            let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0];
            
            let fileName = videoImageUrl; //.stringByDeletingPathExtension
            
            let filePath="\(documentsPath)/\(fileName)";
            
            //saving is done on main thread
            
            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                
                urlData?.writeToFile(filePath, atomically: true);
                print("videoSaved");
            })
            
        }
    })
    
}

我也研究过这个:

let url:NSURL = NSURL(string: fileURL)!;
    
PHPhotoLibrary.sharedPhotoLibrary().performChanges({
    let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(url);
    let assetPlaceHolder = assetChangeRequest!.placeholderForCreatedAsset;
    let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection)
    albumChangeRequest!.addAssets([assetPlaceHolder!])
    }, completionHandler: saveVideoCallBack)

但是我有错误:

无法从文件创建数据(空)

我的“assetChangeRequest”为零。我不明白,因为我的网址是有效的,当我使用浏览器访问它时,它会下载一个快速文件。

我正在使用 Swift,目标版本为 iOS 8.0 分钟。

ios swift
5个回答
49
投票

更新

想要使用 URLSession 更新 Swift 3 的答案,并发现答案已经存在于相关主题here中。使用它。

原答案

下面的代码将视频文件保存到相机胶卷。我重复使用了您的代码,但做了一个小小的更改 - 我删除了

let fileName = videoImageUrl;
,因为它导致文件路径不正确。

我测试了这段代码,它将资产保存到相机胶卷中。您询问要放入

creationRequestForAssetFromVideoAtFileURL
中的内容 - 添加下载视频文件的链接,如下例所示。

let videoImageUrl = "http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_1mb.mp4"

DispatchQueue.global(qos: .background).async {
    if let url = URL(string: urlString),
        let urlData = NSData(contentsOf: url) {
        let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
        let filePath="\(documentsPath)/tempFile.mp4"
        DispatchQueue.main.async {
            urlData.write(toFile: filePath, atomically: true)
            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
            }) { completed, error in
                if completed {
                    print("Video is saved!")
                }
            }
        }
    }
}

12
投票

来自 @Nimble 的 Swift 3 版本代码:

DispatchQueue.global(qos: .background).async {
    if let url = URL(string: urlString),
        let urlData = NSData(contentsOf: url)
    {
        let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0];
        let filePath="\(documentsPath)/tempFile.mp4"
        DispatchQueue.main.async {
            urlData.write(toFile: filePath, atomically: true)
            PHPhotoLibrary.shared().performChanges({
                PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: filePath))
            }) { completed, error in
                if completed {
                    print("Video is saved!")
                }
            }
        }
    }
}

1
投票
PHPhotoLibrary.shared().performChanges({
     PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: video.url!)}) {
     saved, error in
     if saved {
          print("Save status SUCCESS")
     }
}

0
投票

遵循 @Nimble 和 @Yuval Tal 解决方案,最好使用 URLSession dataTask(with:completionHandler:) 方法在写入文件之前下载文件,如 NSData(contentsOf:) Apple 的警告部分所述文档

重要

不要使用此同步初始化程序来请求基于网络的 URL。 对于基于网络的 URL,此方法可以阻塞当前线程 在慢速网络上几十秒,导致用户体验不佳 体验,并且在 iOS 中,可能会导致您的应用程序被终止。

相反,对于非文件 URL,请考虑使用 URLSession 的 dataTask(with:completionHandler:) 方法

正确的实现可能是:

let defaultSession = URLSession(configuration: .default)
var dataTask: URLSessionDataTask? = nil

func downloadAndSaveVideoToGallery(videoURL: String, id: String = "default") {
    DispatchQueue.global(qos: .background).async {
        if let url = URL(string: videoURL) {
            let filePath = FileManager.default.temporaryDirectory.appendingPathComponent("\(id).mp4")
            print("work started")
            self.dataTask = self.defaultSession.dataTask(with: url, completionHandler: { [weak self] data, res, err in
                DispatchQueue.main.async {
                    do {
                        try data?.write(to: filePath)
                        PHPhotoLibrary.shared().performChanges({
                            PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: filePath)
                        }) { completed, error in
                            if completed {
                                print("Saved to gallery !")
                            } else if let error = error {
                                print(error.localizedDescription)
                            }
                        }
                    } catch {
                        print(error.localizedDescription)
                    }
                }
                self?.dataTask = nil
            })
            self.dataTask?.resume()
        }
    }
}

还有一个好处是,您可以通过调用 dataTask 上的相应方法来暂停、恢复和终止下载: URLSessionDataTask .resume() .suspend() .cancel()


0
投票

我是这样做的。 不能 100% 确定这是否是正确的方法,但它确实有效。

func downloadFile(from url: URL) async throws -> URL? {
    let (tempFileURL, _) = try await URLSession.shared.download(from: url)
    
    let destinationURL = tempFileURL.appendingPathExtension("mp4")
    
    if FileManager.default.fileExists(atPath: destinationURL.relativePath) {
        try FileManager.default.removeItem(at: destinationURL)
    }
    
    do {
        try FileManager.default.moveItem(at: tempFileURL, to: destinationURL)
    } catch let error {
        print(error)
    }

    return destinationURL
}

然后在我需要的地方:

guard let file = try await downloadFile(from: downloadUrlURL) else { return }

try await PHPhotoLibrary.shared().performChanges({
    PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: file)
})
© www.soinside.com 2019 - 2024. All rights reserved.