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

如何通过Swift在Firebase中上传PDF、Docx、TXT格式文件?

Hey there! Great job getting your image/video chat app up and running—adding support for PDFs, DOCXs, TXTs, and other documents is totally feasible with Firebase Storage, which handles all those file types without any issues. Let’s walk through how to extend your existing code to make this work smoothly.

Step 1: Update Your Media Sending Function

First, let’s adjust your sendMedia function to handle document files. We’ll add support for passing document URLs, preserve the original file extension (so Firebase knows the file type), and add a dedicated storage path for documents:

// MARK: - Send media message (including documents)
func sendMedia(message: MSMessage, senderRef: FIRDatabaseReference, image: Data?, video: URL?, document: URL?) {
    var mediaTypeText: String?
    var storageRef: FIRStorageReference
    var uniqueID: String

    if let imageData = image {
        mediaTypeText = "Sent you a Photo"
        uniqueID = "\(NSUUID().uuidString).jpg"
        storageRef = DBManager.manager.imageStorageRef.child(Constants.USERS_IMAGES_STORAGE)
            .child(message.senderId).child(uniqueID)
        
        // Upload image data
        storageRef.put(imageData, metadata: nil) { (metadata, error) in
            self.handleUploadCompletion(metadata: metadata, error: error, message: message, senderRef: senderRef, uniqueID: uniqueID)
        }
    } else if let videoURL = video {
        mediaTypeText = "Sent you a Video"
        uniqueID = "\(NSUUID().uuidString).\(videoURL.pathExtension)"
        storageRef = DBManager.manager.videoStorageRef.child(Constants.USERS_VIDEOS_STORAGE)
            .child(message.senderId).child(uniqueID)
        
        // Upload video file
        storageRef.putFile(videoURL, metadata: nil) { (metadata, error) in
            self.handleUploadCompletion(metadata: metadata, error: error, message: message, senderRef: senderRef, uniqueID: uniqueID)
        }
    } else if let docURL = document {
        mediaTypeText = "Sent you a Document"
        // Preserve original file extension for proper MIME type detection
        uniqueID = "\(NSUUID().uuidString).\(docURL.pathExtension)"
        // Add a new constant for document storage path in your Constants struct
        storageRef = DBManager.manager.storageRef.child(Constants.USERS_DOCUMENTS_STORAGE)
            .child(message.senderId).child(uniqueID)
        
        // Upload document file
        storageRef.putFile(docURL, metadata: nil) { (metadata, error) in
            self.handleUploadCompletion(metadata: metadata, error: error, message: message, senderRef: senderRef, uniqueID: uniqueID)
        }
    }
}

// Helper to reduce code duplication in upload completion
private func handleUploadCompletion(metadata: FIRStorageMetadata?, error: Error?, message: MSMessage, senderRef: FIRDatabaseReference, uniqueID: String) {
    if let err = error {
        self.delegate?.errorOccurred(description: err.localizedDescription)
        return
    }
    guard let downloadURL = metadata?.downloadURL() else {
        self.delegate?.errorOccurred(description: "Failed to get download URL")
        return
    }
    self.sendMessage(message: message, senderRef: senderRef, url: downloadURL.absoluteString, fileName: uniqueID)
}

Don’t forget to add the new storage constant to your Constants struct:

struct Constants {
    static let USERS_IMAGES_STORAGE = "user_images"
    static let USERS_VIDEOS_STORAGE = "user_videos"
    static let USERS_DOCUMENTS_STORAGE = "user_documents" // Add this line
    // ... your other constants
}
Step 2: Update Message Observation Logic

Next, we’ll add helper functions to detect document types and update your observeHandler to handle document messages:

First, add these helper checks:

// Helper to check if URL is an image
private func isImage(_ urlString: String) -> Bool {
    let imageExtensions = ["jpg", "jpeg", "png", "gif"]
    guard let url = URL(string: urlString) else { return false }
    return imageExtensions.contains(url.pathExtension.lowercased())
}

// Helper to check if URL is a video
private func isVideo(_ urlString: String) -> Bool {
    let videoExtensions = ["mp4", "mov", "avi", "mkv"]
    guard let url = URL(string: urlString) else { return false }
    return videoExtensions.contains(url.pathExtension.lowercased())
}

// Helper to check if URL is a document
private func isDocument(_ urlString: String) -> Bool {
    let docExtensions = ["pdf", "docx", "doc", "txt", "rtf"]
    guard let url = URL(string: urlString) else { return false }
    return docExtensions.contains(url.pathExtension.lowercased())
}

Then update your observeHandler to route document messages:

// MARK : - Observe Handler
func observeHandler(snapshot: FIRDataSnapshot) {
    guard let data = snapshot.value as? NSDictionary else { return }
    
    guard let senderID = data[Constants.SENDER_ID] as? String,
          let senderName = data[Constants.SENDER_NAME] as? String,
          let sentDate = data[Constants.SENT_DATE] as? Int,
          let text = data[Constants.SENDER_TEXT] as? String,
          let receiverID = data[Constants.RECEIVER_ID] as? String,
          let fileURL = data[Constants.MEDIA_URL] as? String,
          let fileName = data[Constants.FILE_NAME] as? String,
          let messageID = data[Constants.MESSAGE_ID] as? String else {
        debugPrint("\(#function ) error return")
        return
    }
    
    let currentUserID = AuthManager.User.id.value
    let receiveID = UserDefaults.standard.string(forKey: "ReceiveID")
    
    // Verify message is intended for current user
    guard currentUserID == receiverID && receiveID == senderID else { return }
    
    if !text.isEmpty {
        // Handle text message (existing logic)
        let newMessage = MSMessage(senderId: senderID, senderDisplayName: senderName, date: self.millisToDate(sentDate), text: text, receiverID: receiverID, messageID: messageID)
        self.delegate?.messageReceived(message: newMessage, fromNode: false)
    } else if !fileURL.isEmpty {
        guard let mediaURL = URL(string: fileURL) else { return }
        
        if self.isImage(fileURL) {
            self.getImage(mediaURL, senderID, senderName, receiverID, sentDate, messageID, fileName: fileName)
        } else if self.isVideo(fileURL) {
            self.getVideo(mediaURL, senderID, senderName, receiverID, sentDate, messageID, fileName: fileName)
        } else if self.isDocument(fileURL) {
            self.getDocument(mediaURL, senderID, senderName, receiverID, sentDate, messageID, fileName: fileName)
        }
    }
}
Step 3: Add Document Receiving Function

Create a function to handle document messages, similar to your existing getVideo function. Since documents aren’t displayed directly, we’ll pass the file details to your delegate for user interaction (like opening/saving):

// MARK: - Get Document
func getDocument(_ mediaURL: URL, _ senderID: String, _ senderName: String, _ receiverID: String, _ sentDate: Int, _ messageID: String, fileName: String, completion: ((MSMessage)->())? = nil) {
    DispatchQueue.global().async {
        // Create your message object with document details
        let newMessage = MSMessage(
            senderId: senderID,
            displayName: senderName,
            media: nil, // Or create a custom media item for document previews
            date: self.millisToDate(sentDate),
            receiverID: receiverID,
            messageID: messageID,
            mediaURL: mediaURL,
            fileName: fileName
        )
        
        DispatchQueue.main.async {
            if let completion = completion {
                completion(newMessage)
            } else {
                // Add a new delegate method for document messages, or use existing mediaReceived
                self.delegate?.documentReceived(message: newMessage)
            }
        }
    }
}
Step 4: Add Document Picker to Your UI

Let users select documents from their device using UIDocumentPickerViewController:

func presentDocumentPicker() {
    // Allow selection of any file type, or restrict to specific types if needed
    let documentPicker = UIDocumentPickerViewController(documentTypes: ["public.data"], in: .import)
    documentPicker.delegate = self
    documentPicker.allowsMultipleSelection = false
    present(documentPicker, animated: true)
}

// Implement UIDocumentPickerDelegate
extension YourChatViewController: UIDocumentPickerDelegate {
    func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
        guard let selectedURL = urls.first else { return }
        
        // Create your MSMessage object with appropriate details
        let message = MSMessage(
            senderId: AuthManager.User.id.value,
            senderDisplayName: AuthManager.User.name.value,
            date: Date(),
            text: "", // Empty text since it's a document message
            receiverID: UserDefaults.standard.string(forKey: "ReceiveID") ?? "",
            messageID: NSUUID().uuidString
        )
        
        // Get your message reference (use existing logic here)
        let senderRef = DBManager.manager.messagesRef.child(/* your chat node path */)
        
        // Send the document
        sendMedia(message: message, senderRef: senderRef, image: nil, video: nil, document: selectedURL)
        
        controller.dismiss(animated: true)
    }
    
    func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
        controller.dismiss(animated: true)
    }
}
Key Notes
  • Firebase Storage Rules: Ensure your storage rules allow authenticated users to read/write to the user_documents path (you probably already have similar rules for images/videos).
  • File Size: Keep an eye on Firebase Storage’s free tier limits—you can add size checks if you want to restrict large uploads.
  • Delegate Updates: Add a documentReceived method to your delegate protocol to handle incoming document messages, or adjust your existing mediaReceived method to detect document types.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:28:36