Swift新手求助:iOS本地PDF/DOCX/图片上传服务器失败(415错误)
Swift上传文件遇到415错误?看这里!
嘿,作为Swift新手碰到这个问题太正常啦!415状态码说白了就是服务器在喊“我看不懂你发过来的内容格式啊!”,大概率是你请求的Content-Type没设置对,或者上传文件的表单数据构造出了问题。下面给你一步步解决:
核心原因分析
415(Unsupported Media Type)通常出现在这几种情况:
- 没有用multipart/form-data格式上传文件(这是文件上传的标准格式)
- 请求头的
Content-Type没带正确的边界字符串(boundary) - 文件的MIME类型和服务器期望的不匹配(比如把PDF标成了image/jpeg)
修正后的完整代码示例
先给你把代码改好,你可以直接参考:
import UIKit import MobileCoreServices // 要导入这个框架来获取MIME类型 func uploadFileToServer(completed: @escaping () -> ()) { guard let theTitle = labelTitle.text, !theTitle.isEmpty else { print("标题不能为空") completed() return } guard let url = URL(string: "http://www.--------.com/assignment/post") else { print("无效的服务器URL") completed() return } // 假设你已经拿到了要上传的本地文件URL,这里替换成你自己的文件获取逻辑 guard let fileURL = getLocalFileURL() else { print("找不到本地文件") completed() return } var request = URLRequest(url: url) request.httpMethod = "POST" // 1. 设置正确的请求头 let boundary = "Boundary-\(UUID().uuidString)" // 生成随机边界字符串 request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") // 如果服务器需要授权token,把你的token加上 request.setValue("cf7ab8c9d4efae82b575...", forHTTPHeaderField: "Authorization") // 2. 构造multipart表单数据 var body = Data() // 添加标题参数(如果服务器需要的话) body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"title\"\r\n\r\n".data(using: .utf8)!) body.append("\(theTitle)\r\n".data(using: .utf8)!) // 添加文件数据 do { let fileData = try Data(contentsOf: fileURL) let fileName = fileURL.lastPathComponent let mimeType = getMimeType(for: fileURL) body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) body.append("Content-Type: \(mimeType)\r\n\r\n".data(using: .utf8)!) body.append(fileData) body.append("\r\n".data(using: .utf8)!) } catch { print("读取本地文件失败:\(error.localizedDescription)") completed() return } // 结束表单边界 body.append("--\(boundary)--\r\n".data(using: .utf8)!) request.httpBody = body // 3. 发起请求 let task = URLSession.shared.dataTask(with: request) { data, response, error in defer { completed() } // 不管成功失败都调用完成回调 if let error = error { print("请求出错:\(error.localizedDescription)") return } guard let httpResponse = response as? HTTPURLResponse else { print("无效的服务器响应") return } print("响应状态码:\(httpResponse.statusCode)") if let data = data, let responseStr = String(data: data, encoding: .utf8) { print("服务器返回内容:\(responseStr)") } } task.resume() } // 辅助函数:获取本地文件URL(替换成你自己的文件选择逻辑,比如UIImagePickerController获取的URL) func getLocalFileURL() -> URL? { // 示例:这里只是模拟,你需要替换成实际的文件获取代码 let documentsDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] return URL(fileURLWithPath: documentsDir).appendingPathComponent("test.pdf") } // 辅助函数:获取文件的MIME类型 func getMimeType(for fileURL: URL) -> String { if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileURL.pathExtension as CFString, nil)?.takeRetainedValue() { if let mimeType = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() { return mimeType as String } } // 如果识别不出来,默认返回二进制流类型 return "application/octet-stream" }
关键注意点
- 必须用multipart/form-data格式:上传文件绝对不能用application/json这类格式,服务器根本识别不了文件内容
- 边界字符串要唯一:用UUID生成的边界能避免和文件内容冲突,确保服务器能正确拆分表单部分
- MIME类型要准确:不同文件类型对应不同的MIME,比如:
- PDF:
application/pdf - DOCX:
application/vnd.openxmlformats-officedocument.wordprocessingml.document - JPG:
image/jpeg - PNG:
image/png
- PDF:
- 参数要放在表单里:如果服务器需要标题、用户ID这类参数,一定要加到multipart表单中,不能直接拼在URL或者用JSON传
内容的提问来源于stack exchange,提问作者TheSwiftGuy77
相关产品推荐
相关产品推荐

