I am trying to upload image or video using chunk uploading steps step 1 and 2 i.e INIT and APPEND works fine (I also checked the logs) but in FINALIZE step getting 400 bad request. Can someone please tell me what I am missing or doing something wrong?
STEP1: INIT
const splitBuffer = require('../splitBuffer');
const fetch = require('cross-fetch');
const crypto = require('crypto');
const OAuth = require('oauth-1.0a');
const oauth = OAuth({
consumer: {
key: process.env.TWITTER_CONSUMER_KEY,
secret: process.env.TWITTER_CONSUMER_SECRET
},
signature_method: 'HMAC-SHA1',
hash_function: (baseString, key) => crypto.createHmac('sha1', key).update(baseString).digest('base64')
});
const token = {
key: '',
secret: ''
};
const initUpload = async (buffer) => {
// Init
const bufferLength = Buffer.byteLength(buffer);
const data = { 'command': 'INIT', 'total_bytes': bufferLength, 'media_type': 'image/png' };
const requestBody = {
url: `https://upload.twitter.com/1.1/media/upload.json`,
method: 'POST',
data: data
}
const authHeader = oauth.toHeader(oauth.authorize(requestBody, token));
const body = percentEncode(querystring.stringify(data));
const result = await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
method: 'POST',
headers: {
Authorization: authHeader["Authorization"],
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
body: body,
});
return result.json();
}
STEP 2: APPEND
const appendMedia = async (buffer, chunkSize, media_id_string) => {
const parts = splitBuffer(buffer, chunkSize);
const uploads = parts.map(async (part, index) => {
const data = { 'command': 'APPEND', 'media_id': media_id_string, 'segment_index': index, media_data: part }
const requestBody = {
url: `https://upload.twitter.com/1.1/media/upload.json`,
method: 'POST',
data: data
}
const authHeader = oauth.toHeader(oauth.authorize(requestBody, token));
const body = percentEncode(querystring.stringify(data));
const responseData = await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
method: 'POST',
headers: {
Authorization: authHeader["Authorization"],
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
body: body,
});
return responseData;
});
const finalResult = await Promise.all(uploads).then(() => media_id_string);
return finalResult;
}
STEP 3: FINALIZE
const finalize = async (media_id_string) => {
const data = { 'command': 'FINALIZE', 'media_id': media_id_string }
const requestBody = {
url: `https://upload.twitter.com/1.1/media/upload.json`,
method: 'POST',
data: data
}
const authHeader = oauth.toHeader(oauth.authorize(requestBody, token));
const body = percentEncode(querystring.stringify(data));
const result = await fetch(`https://upload.twitter.com/1.1/media/upload.json`, {
method: 'POST',
headers: {
Authorization: authHeader["Authorization"],
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
body: body
});
const answer = await result.json();
}
Routes:
router.post('/twitter/media', async (req, res) => {
try {
const image = await axios.get('https://ik.imagekit.io/XXXXXXXXX/test-upload_9OP0H7LEl.png', { responseType: 'arraybuffer' });
const base64Image = new Buffer.from(image.data).toString('base64');
const result = await initUpload(base64Image);
const { media_id_string } = result;
console.log("init media_id_string", media_id_string);
const result2 = await appendMedia(base64Image, 1024, media_id_string);
const response = await finalize(media_id_string);
console.log("response", response);
res.status(200).send({ status: "ok" });
} catch (error) {
console.log("error", error)
res.status(403).json({ message: "Missing, invalid, or expired tokens" });
}
});
I referred few stackoverflow posts and twitter community answers but none of them is working for me. I tried to increase/decrease file chunk size. I also tried to use multipart/form-data instead of application/x-www-form-urlencoded for step 3 FINALIZE but that is also not working. Image which I am using is 1.6mb size. Please suggest what I am missing. Thanks.
Note: In docs one thing is mentioned:
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``` am I facing issue because of this ??