Hacky media upload to mastodon in Swift, does not post
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Hacky media upload to mastodon, does not post | |
// A condensation of things I learned about Swift and Mastodon API w/ @carlynorama | |
// 12 Feb 2023 - @todbot / Tod Kurt | |
// | |
// Compile with: | |
// swiftc masto-media-up.swift -o masto-media-up | |
// | |
// Requires: | |
// - existing Mastodon account on a given server ("--url") | |
// - authorization token from a created app in Mastodon ("--auth") | |
// - a file to upload ("--file") | |
// | |
// Invoke with: | |
// ./masto-media-up --url https://mastodon.social/api/v2/media \ | |
// --auth cUW2ZBs5BWu-IopQ05ImudTnTP4LLrD2KVrLV9S1KE0 \ | |
// --file myimage.jpg | |
// And it returns with the JSON response from the server | |
// | |
// Uses bits stolen from: | |
// - https://github.com/carlynorama/APIng/tree/main/Sources/APIng | |
// - https://igomobile.de/2020/06/16/swift-upload-a-file-with-multipart-form-data-in-ios-using-uploadtask-and-urlsession/ | |
// | |
import Foundation | |
import UniformTypeIdentifiers | |
var fileStr = "fractal5x5.jpg" | |
var urlStr = "https://httpbin.org/post" // "http://localhost:8080/" | |
var yourAuthToken = "fakeyauth12345678ABCDEFGH12345" | |
for pos in 0..<CommandLine.arguments.count { | |
if( CommandLine.arguments[pos] == "--url" ) { | |
urlStr = CommandLine.arguments[pos+1] | |
} | |
if( CommandLine.arguments[pos] == "--file" ) { | |
fileStr = CommandLine.arguments[pos+1] | |
} | |
if( CommandLine.arguments[pos] == "--auth" ) { | |
yourAuthToken = CommandLine.arguments[pos+1] | |
} | |
} | |
print("Uploading to...\n\turl =",urlStr,"\n\tuploading file =",fileStr) | |
let url = URL(string:urlStr)! | |
let fileURL = URL(fileURLWithPath:fileStr) | |
let boundary = UUID().uuidString // generate boundary string using a unique string | |
public func mimeType(for path: String) -> String { | |
let pathExtension = URL(fileURLWithPath: path).pathExtension as String | |
return UTType(filenameExtension: pathExtension)?.preferredMIMEType ?? "application/octet-stream" | |
} | |
// Set the URLRequest to POST and to the specified URL | |
var request = URLRequest(url: url ) | |
request.httpMethod = "POST" | |
request.addValue("Bearer \(yourAuthToken)", forHTTPHeaderField: "Authorization") | |
// Content-Type is multipart/form-data, the same as submitting form data with file upload in a web browser | |
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") | |
let fileName = fileURL.lastPathComponent | |
let mimetype = mimeType(for: fileName) | |
let paramName = "file" | |
let fileData = try? Data(contentsOf: fileURL) | |
var data = Data() | |
// Add the file data to the raw http request data | |
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!) | |
data.append("Content-Disposition: form-data; name=\"\(paramName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!) | |
data.append("Content-Type: \(mimetype)\r\n\r\n".data(using: .utf8)!) | |
data.append(fileData!) | |
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!) | |
// do not forget to set the content-length! | |
request.setValue(String(data.count), forHTTPHeaderField: "Content-Length") | |
let (responseData, response) = try await URLSession.shared.upload(for: request, from: data, delegate: nil) | |
print("Upload done.\nResponse from server:\n") | |
print(String(data:responseData, encoding: .utf8) ?? "Nothing") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Notes: