Skip to content

Instantly share code, notes, and snippets.

@ECHibiki
Last active February 28, 2018 04:45
Show Gist options
  • Save ECHibiki/27a6fe234436af7015699dea187cd81b to your computer and use it in GitHub Desktop.
Save ECHibiki/27a6fe234436af7015699dea187cd81b to your computer and use it in GitHub Desktop.
Twitter media upload with php curl

Am a bit frustrated that the docs have been inaccurate for over 3 years apparently.

You need to use everything for the INIT... use all of the parameters not just the oauth_* ones The following php curl code got me past the error 32

Edit: The above is also true for APPEND and FINALIZE. Please check my twitter bot for info.

	$media_api = "https://upload.twitter.com/1.1/media/upload.json";
	
	$settings = fopen("settings/keys.ini","r");
	$keys = array();
	while(!feof($settings)){
		$line = fgets($settings);
		$key = substr($line,0,strpos($line, ":"));
				//eat last character
		$value = trim(substr($line, strpos($line, ":")+1));
		$keys[$key] = $value;
	}
	$consumer_key = $keys["consumer_key"];
	$random_value = substr(( base64_encode(rand(1000000000,10000000000000)) . base64_encode(rand(1000000000,10000000000000)) . base64_encode(rand(1000000000000,100000000000000))), 0, 32);
	$random_value =  str_replace("/", "5", str_replace("=", "2", $random_value));
	$method = "HMAC-SHA1";
	$timestamp = time();
	$access_token =  $keys["access_token"];
	$oauth_version = "1.0";
	
	//Secrets
	$consumer_secret = $keys["consumer_secret"];
	$oauth_secret = $keys["oauth_secret"];
	
	//post data
	$command = "INIT";
	$postfield_string = "command=$command&total_bytes=$size&media_type=$mime";
	$msg_len = (strlen($postfield_string));
	
	//header data
		  // BUILD SIGNATURE			
		$request = array(
			"base_url" => $media_api,
			"request_method" => "POST"
			);
		$message = array(
			"command" => "$command",
			"total_bytes" => "$size",
			"media_type" => "$mime"
			);			  
		$params = array(
			"oauth_version" => "$oauth_version",
			"oauth_nonce" => "$random_value",
			"oauth_token" => "$access_token",
			"oauth_timestamp" => "$timestamp",
			"oauth_consumer_key" => "$consumer_key",
			"oauth_signature_method" => "$method"
			);
		$secrets = array(
			"consumer_secret" => "$consumer_secret",
			"oauth_secret" => "$oauth_secret"
			);
									
		$request_method = strtoupper($request["request_method"]);
		$base_url = urlEncodeCustom($request["base_url"]);
		$paramter_string = urlEncodeCustom("command=" . $message["command"] . "&media_type=" . $message["media_type"] . "&oauth_consumer_key=" . $params["oauth_consumer_key"]
		 . "&oauth_nonce=" . $params["oauth_nonce"] . "&oauth_signature_method=" . $params["oauth_signature_method"] . "&oauth_timestamp=" . $params["oauth_timestamp"]."&oauth_token=" . $params["oauth_token"] .
		 "&oauth_version=" .$params["oauth_version"] . "&total_bytes=" . $message["total_bytes"]); 
		
		$base_string = ($request_method . "&" .  $base_url  . "&" . $paramter_string);
				
		$secret_string = $secrets["consumer_secret"] . "&" . $secrets["oauth_secret"];
		
		$signature =  strtoupper(hash_hmac("SHA1",$base_string, $secret_string));	
		
		$split_hex_signature = "";
		foreach(str_split($signature, 2) as $split){
			$split_hex_signature .= chr(hexdec($split));
		}

		$signature = urlEncodeCustom(base64_encode($split_hex_signature));
	
	$header_data = array("Accept: */*", "Connection: close","User-Agent: VerniyXYZ-CURL" ,"Content-Transfer-Encoding: base64",
												"Content-Type: application/x-www-form-urlencoded;charset=UTF-8", 
												"Content-Length: $msg_len", "Host: upload.twitter.com",
"Authorization: OAuth oauth_consumer_key=\"$consumer_key\",oauth_nonce=\"$random_value\",oauth_signature=\"$signature\",oauth_signature_method=\"$method\",oauth_timestamp=\"$timestamp\",oauth_token=\"$access_token\",oauth_version=\"$oauth_version\""																				
												);									
	//request
	$curl = curl_init($media_api);
	curl_setopt($curl, CURLOPT_POST, 1);
	curl_setopt($curl, CURLOPT_HTTPHEADER, $header_data);
	curl_setopt($curl, CURLOPT_POSTFIELDS, $postfield_string);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
	echo " ++ ";
	$media_id = curl_exec($curl);
	echo $media_id;
	echo " . <br/>";

Resulted in: {"media_id":960118852607655937,"media_id_string":"960118852607655937","expires_after_secs":86399} .


GIST RELATES TO THE FOLLOWING: There are a few important concepts to understand when using the POST media/upload endpoint. Uploading media with OAuth can be a bit tricky, so we’ve outlined some things to keep in mind as well as a working sample of how to use this endpoint here. Keep in mind Because the method uses multipart POST, OAuth is handled differently. POST or query string parameters are not used when calculating an OAuth signature basestring or signature. Only the oauth_* parameters are used. You may attach up to 4 photos, 1 animated GIF or 1 video in a Tweet. The image passed should be the raw binary of the image or binary base64 encoded, no need to otherwise encode or escape the contents as long as the Content-Type is set appropriately (when in doubt: application/octet-stream). When posting base64 encoded images, be sure to set the “Content-Transfer-Encoding: base64” on the image part of the message. Multi-part message boundaries must be on their own line and terminated by a CRLF. For working examples of how to POST using this endpoint, we recommend testing with twurl. Also take a look at the Twitter Libraries available. Use the media_id_string provided in the API response from Javascript and other languages that cannot accurately represent a long integer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment