code icon Code

Upload File to Slack

Upload files to Slack channels using the three-step upload process

Source Code

import fs from 'fs';
import path from 'path';

/**
 * Upload a file to a Slack channel using the three-step upload process
 * @param {string} channelId - The channel ID to upload to (e.g., 'C09R5QUC527')
 * @param {string} filePath - Path to the file to upload
 * @param {string} slackToken - Slack API token
 * @param {Object} options - Optional parameters
 * @param {string} options.title - Title for the file
 * @param {string} options.initialComment - Optional comment to add with the file
 * @returns {Promise<Object>} Upload result with file ID, name, and permalink
 */
async function uploadFileToSlack(channelId, filePath, slackToken, options = {}) {
  // Read the file
  const fileBuffer = fs.readFileSync(filePath);
  const fileName = path.basename(filePath);
  const fileSize = fileBuffer.length;


  // Step 1: Get upload URL from Slack
  const uploadUrlFormData = new FormData();
  uploadUrlFormData.append('filename', fileName);
  uploadUrlFormData.append('length', fileSize.toString());

  const uploadUrlResponse = await fetch('https://slack.com/api/files.getUploadURLExternal', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${slackToken}`
    },
    body: uploadUrlFormData
  });

  const uploadUrlData = await uploadUrlResponse.json();

  if (!uploadUrlData.ok) {
    throw new Error(`Failed to get upload URL: ${uploadUrlData.error}`);
  }

  const { upload_url, file_id } = uploadUrlData;

  // Step 2: Upload file contents to the provided URL
  const uploadResponse = await fetch(upload_url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/octet-stream'
    },
    body: fileBuffer
  });


  if (!uploadResponse.ok) {
    throw new Error(`Failed to upload file contents: ${uploadResponse.statusText}`);
  }

  // Step 3: Complete the upload and share to channel
  const completeFormData = new FormData();

  const fileData = {
    id: file_id
  };

  if (options.title) {
    fileData.title = options.title;
  }

  completeFormData.append('files', JSON.stringify([fileData]));
  completeFormData.append('channel_id', channelId);

  if (options.initialComment) {
    completeFormData.append('initial_comment', options.initialComment);
  }

  const completeResponse = await fetch('https://slack.com/api/files.completeUploadExternal', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${slackToken}`
    },
    body: completeFormData
  });

  const completeData = await completeResponse.json();

  if (!completeData.ok) {
    throw new Error(`Failed to complete upload: ${completeData.error}`);
  }

  const uploadedFile = completeData.files?.[0];
  console.log(`[DEBUG] Successfully uploaded file: ${uploadedFile?.name} (ID: ${uploadedFile?.id})`);

  return {
    success: true,
    fileId: uploadedFile?.id,
    fileName: uploadedFile?.name,
    permalink: uploadedFile?.permalink
  };
}

export { uploadFileToSlack };

/**
 * Run this script directly from the command line:
 * bun send_file.js <channelId> <filePath> <slackToken> [title] [initialComment]
 *
 * Example:
 * bun send_file.js C09R5QUC527 ./document.pdf xoxb-your-token "My Document" "Here is the file"
 */
if (import.meta.main) {
  const args = process.argv.slice(2);

  if (args.length < 3) {
    console.error('Usage: bun send_file.js <channelId> <filePath> <slackToken> [title] [initialComment]');
    console.error('Example: bun send_file.js C09R5QUC527 ./document.pdf xoxb-your-token "My Document" "Here is the file"');
    process.exit(1);
  }

  const [channelId, filePath, slackToken, title, initialComment] = args;

  const options = {};
  if (title) options.title = title;
  if (initialComment) options.initialComment = initialComment;

  uploadFileToSlack(channelId, filePath, slackToken, options)
    .then(result => {
      console.log('Upload successful!');
      console.log(`File ID: ${result.fileId}`);
      console.log(`File Name: ${result.fileName}`);
      console.log(`Permalink: ${result.permalink}`);
    })
    .catch(error => {
      console.error('Upload failed:', error.message);
      process.exit(1);
    });
}