August 12, 2025

How to import a file from your MID Server in 2 Flow Designer Steps!

The Challenge: When APIs Cost Too Much

Picture this: you need to import data from a third-party system, but their API costs $25,000 annually. Meanwhile, they'll let you export the same data for free as a file. What option would you choose?

This exact scenario recently came up with a client using Concur. While we'd normally build a sleek API integration (and trust us, we love integrations at Semaphore Partners), the astronomical API pricing forced us to get creative. The client could only drop files on their MID Server, so we needed a solution that worked within those constraints.


A Bit of History

Back in 2014, there was a fantastic app called Remote File Importer on ServiceNow Share (thank you to SNC Guru Mark Stanger!). It solved this exact problem, but ServiceNow Share is no longer active, and finding those old update sets is like searching for buried treasure.

The good news? ServiceNow's newer capabilities make this easier than ever.

Our Solution: A Two-Step Flow

We created a custom Flow Action that grabs files from the MID Server and attaches them to a Data Source in ServiceNow. Once attached, the Data Source treats it like any manually uploaded file and imports it seamlessly.

Here's how it works:

Step 1: MID Server Script

The first step runs a script on the MID Server to locate and read the target file. This step handles all the file system operations and returns the file's base64 encoded content to ServiceNow.

(function execute(inputs, outputs) {
  function getMimeType(extension) {
    switch (extension) {
      case 'jpg':
      case 'jpeg':
        return 'image/jpeg';
      case 'png':
        return 'image/png';
      case 'gif':
        return 'image/gif';
      case 'xlsx':
        return 'application/xlsx';
      default:
        return 'application/octet-stream';
    }
  }
  
  //const file = new Packages.java.io.File(inputs.filePath);
  const file = new Packages.java.io.File(
    inputs.file_path+"\\"+inputs.file_name
  );

  var result = [];

  // Read file and encode to base64
  var fileInputStream = null;
  var base64Data = "";

  var skipReason = "";
  var fileName = "";
  var fileSize = 0;
  var fileExtension = "";
  var fileMimeType = "";
  var fileLastModified = "";
  var base64Data = "";

  // Get basic file info even if we skip it
  try {
    fileName = file.getName() + "";
    fileSize = file.length();
    fileExtension =
      fileName.indexOf(".") !== -1
        ? fileName.split(".").pop().toLowerCase()
        : "";
    fileMimeType = getMimeType(fileExtension);
    fileLastModified = file.lastModified();
  } catch (infoError) {
    skipReason = "Unable to read file information";
  }

  try {
    fileInputStream = new Packages.java.io.FileInputStream(file);
    const bytes = new Packages.java.lang.reflect.Array.newInstance(
      Packages.java.lang.Byte.TYPE,
      fileSize
    );

    fileInputStream.read(bytes);
    base64Data = Packages.java.util.Base64.getEncoder().encodeToString(bytes);
  } finally {
    if (fileInputStream) {
      fileInputStream.close();
    }
  }
   outputs.result = JSON.stringify({
      fileName: fileName,
      fileSize: fileSize,
      fileExtension: fileExtension,
      fileMimeType: fileMimeType,
      fileLastModified: fileLastModified,
      base64Data: base64Data
    });
})(inputs, outputs);

Step 2: Server Script

The second step takes the file content from Step 1 and creates an attachment on your Data Source using server-side code. This transforms the raw file data into a properly formatted ServiceNow attachment.

(function execute(inputs, outputs) {
  const fileInfo = JSON.parse(inputs.attachment);
  const data_source = inputs.data_source;
  outputs.file_name_2 = fileInfo.fileName;

  const attachment = new GlideSysAttachment();

  attachment.deleteAll(data_source);

  var binData = GlideStringUtil.base64DecodeAsBytes(fileInfo.base64Data);

  const attachment_id = attachment.write(
    data_source,
    fileInfo.fileName,
    fileInfo.fileMimeType,
    binData
  );
  outputs.attachment_id = attachment_id;
})(inputs, outputs);

Bonus: Tie it to a Data Source

Want to take this further? You can integrate this flow directly with your data import process using this snippet as a Scheduled Import Set pre-script:

The flow automatically detects which MID Server your Data Source is configured to use, ensuring it works correctly across different environments.

(function() {

    try {
        var inputs = {};

        var data_source = new GlideRecord("sys_data_source");
        data_source.get(DATA_SOURCE_SYS_ID);

        inputs['file_name'] = "my_report.xlsx"; // String 
        inputs['file_path'] = "\\\\myFileshare\\ServiceNow"; // String 
        inputs['data_source'] = data_source; // GlideRecord of table: sys_data_source 

        var result = sn_fd.FlowAPI.getRunner().action('global.import_excel_from_mid_server').inForeground().withInputs(inputs).run();
        var outputs = result.getOutputs();

        // Current action has no outputs defined.		
    } catch (ex) {
        var message = ex.getMessage();
        gs.error(message);
    }
})();

Why This Matters

This approach gives you the best of both worlds: the cost savings of file-based imports with the automation and reliability of a proper integration. Plus, it's built using ServiceNow's native Flow Designer, making it maintainable and upgradeable.

Have you run into similar API pricing challenges? We'd love to hear how you've solved them. Sometimes the most elegant solution isn't the most obvious one.


Need help building custom integrations or solving tricky ServiceNow challenges? Semaphore Partners specializes in creative solutions that work within real-world constraints.