iOS 13 为从 UIImagePickController 中选择的文件添加 "trim." 前缀

iOS 13 adds "trim." prefix to files selected from UIImagePickController

iOS 13 从照片中选择文件时添加trim.前缀:

file:///private/var/mobile/Containers/Data/PluginKitPlugin/FPDLKFHEQ-4T56-3456-HTE2-39EK2KDJUR/tmp/trim.DFLSPD0F-32RE-UYI8-DFHA-DPFLEOW098UH.MOV

之前iOS-13是这样的:

file:///private/var/mobile/Containers/Data/PluginKitPlugin/FPDLKFHEQ-4T56-3456-HTE2-39EK2KDJUR/tmp/DFLSPD0F-32RE-UYI8-DFHA-DPFLEOW098UH.MOV

这是将文件 URL 传递给 alamofire 以将文件上传到后端服务器时出现的问题。它会导致 "unknown error" 并且上传失败。也许 alamofire 在使用那个小前缀时遇到了麻烦?

有解决办法吗?

也许您现在应该重命名您的文件。

 var lastPathComponent = yourFileURL.lastPathComponent
 if lastPathComponent.lowercased().contains("trim") { 

 lastPathComponent = lastPathComponent.replacingOccurrences(of: "trim.", with: "")

   try? yourFileURL.setResourceValue(lastPathComponent, forKey: URLResourceKey.nameKey)
        // considering your fileURL is an nsurl
  }

是的,这里有人遇到过。我会扩展答案。

您可能需要将该视频复制到临时文件夹中,因此在您的 UIImagePickerControllerDelegate 方法中:

public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
    if let mediaType = info[.mediaType] as? String,
        mediaType == "public.movie",
        let trimVideoURL = info[.mediaURL] as? URL {
        if #available(iOS 13, *) {
            // 1
            let urlSlices = trimVideoURL.relativeString.split(separator: ".")
            // 2
            let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
            // 3
            let targetFileURL = tempDirectoryURL.appendingPathComponent(String(urlSlices[1])).appendingPathExtension(String(urlSlices[2]))
            do {
                // 4
                try FileManager.default.copyItem(at: trimVideoURL, to: targetFileURL)
            } catch {
                print(error.localizedDescription)
            }
            // use the video file via targetFileURL path
        }
    }
}
  1. 这里有 trimVideURL,需要按 . 进行切片 file:///private/var/mobile/Containers/Data/PluginKitPlugin/ECD6D7FD-35A8-47E2-8323-808454A32C37/tmp/trim.919BDBB7-E711-49AA-BB84-89E9B0416045.MOV

  2. 创建临时目录:
    file:///private/var/mobile/Containers/Data/Application/FCD3E053-82DF-4EAE-86BA-4C65880D5B90/tmp/

  3. 在临时文件夹中编写目标文件名:file:///private/var/mobile/Containers/Data/Application/FCD3E053-82DF-4EAE-86BA-4C65880D5B90/tmp/919BDBB7-E711-49AA-BB84-89E9B0416045.MOV

  4. 最后使用 FileManager

  5. 将项目复制到您的临时文件夹
  6. 准备好后,您可以使用以下方法删除文件:

do {
    try FileManager.default.removeItem(at: targetFileURL)
} catch {
    print(error.localizedDescription)
}

Good article关于临时文件

此外,您可以将此测试项目作为参考,只需将其复制到新创建的项目中即可测试功能:

import UIKit
import WebKit

class ViewController: UIViewController, UINavigationControllerDelegate {
    var imageView: UIImageView!
    var videoView: WKWebView!
    var selectButton: UIButton!
    var deleteButton: UIButton!

    var pickerController: UIImagePickerController?
    var targetFileURL: URL?

    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
        setupPickerController()
    }

    func configureUI() {
        imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        videoView = WKWebView()
        videoView.translatesAutoresizingMaskIntoConstraints = false
        selectButton = UIButton(type: .system)
        selectButton.setTitle("Select", for: .normal)
        selectButton.addTarget(self, action: #selector(showImagePicker), for: .touchUpInside)
        selectButton.translatesAutoresizingMaskIntoConstraints = false
        deleteButton = UIButton(type: .system)
        deleteButton.setTitle("Delete", for: .normal)
        deleteButton.addTarget(self, action: #selector(deleteCurrentTempFile), for: .touchUpInside)
        deleteButton.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(imageView)
        view.addSubview(videoView)
        view.addSubview(selectButton)
        view.addSubview(deleteButton)

        imageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
        imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        imageView.heightAnchor.constraint(equalToConstant: 250.0).isActive = true

        videoView.topAnchor.constraint(equalTo: imageView.bottomAnchor).isActive = true
        videoView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        videoView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        videoView.heightAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true

        deleteButton.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        deleteButton.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        deleteButton.heightAnchor.constraint(equalToConstant: 60.0).isActive = true
        deleteButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true

        selectButton.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        selectButton.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        selectButton.heightAnchor.constraint(equalTo: deleteButton.heightAnchor).isActive = true
        selectButton.bottomAnchor.constraint(equalTo: deleteButton.topAnchor).isActive = true

        view.layoutIfNeeded()
    }

    func setupPickerController() {
        self.pickerController = UIImagePickerController()
        self.pickerController?.delegate = self
        if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary) {
            self.pickerController?.mediaTypes = mediaTypes
        }
    }

    private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
        guard UIImagePickerController.isSourceTypeAvailable(type) else {
            return nil
        }

        return UIAlertAction(title: title, style: .default) { [unowned self] _ in
            self.pickerController?.sourceType = type
            if let pickerController = self.pickerController {
                self.present(pickerController, animated: true)
            }
        }
    }

    @IBAction func showImagePicker() {
        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        if let action = self.action(for: .photoLibrary, title: "Photo library") {
            alertController.addAction(action)
        }
        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        present(alertController, animated: true)
    }

    @IBAction func deleteCurrentTempFile() {
        if let url = targetFileURL,
            FileManager.default.fileExists(atPath: url.path) {
            removeTemporaryFile(at: url)
        }
    }

    func didSelectMedia(image: UIImage?, videoURL: URL?) {
        if let image = image {
            imageView.image = image
        } else if let videoURL = videoURL {
            let request = URLRequest(url: videoURL)
            videoView.load(request)
            targetFileURL = videoURL
        }
    }

    // remove it when you done
    func removeTemporaryFile(at url: URL) {
        do {
            try FileManager.default.removeItem(at: url)
        } catch {
            print(error.localizedDescription)
        }
        targetFileURL = nil
    }
}

extension ViewController: UIImagePickerControllerDelegate {

    public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        if let mediaType = info[.mediaType] as? String {
            if mediaType == "public.movie", let trimVideoURL = info[.mediaURL] as? URL {
                if #available(iOS 13, *) {
                    let urlSlices = trimVideoURL.relativeString.split(separator: ".")
                    let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
                    let targetFileURL = tempDirectoryURL.appendingPathComponent(String(urlSlices[1])).appendingPathExtension(String(urlSlices[2]))
                    do {
                        try FileManager.default.copyItem(at: trimVideoURL, to: targetFileURL)
                    } catch {
                        print(error.localizedDescription)
                    }
                    self.didSelectMedia(image: nil, videoURL: targetFileURL)
                } else {
                    self.didSelectMedia(image: nil, videoURL: trimVideoURL)
                }
            } else if mediaType == "public.image", let image = info[.originalImage] as? UIImage {
                self.didSelectMedia(image: image, videoURL: nil)
            }
        }
        picker.dismiss(animated: true, completion: nil)
    }
}