Upload File: Multipart-formdata

Hi, Thanks for the work you do. I’m able to download a file but then want to upload the file to the Box service. The binary data is in in a binary node.

image

The CURL call would like this:

curl -X POST https://upload.box.com/api/2.0/files/content \
     -H 'Authorization: Bearer <ACCESS_TOKEN>'
     -H "Content-Type: multipart/form-data" \
     -F attributes='{"name":"Photo.jpg", "parent":{"id":"11446498"}}' \
     -F [email protected]<FILE_NAME>

I have configured an HTTP Request as follows. It throws no errors, but doesn’t upload the data either.

{
  "nodes": [
    {
      "parameters": {
        "authentication": "headerAuth",
        "requestMethod": "POST",
        "url": "=https://upload.box.com/api/2.0/files/content",
        "options": {
          "bodyContentType": "multipart-form-data"
        },
        "bodyParametersUi": {
          "parameter": [
            {
              "name": "attributes",
              "value": "={\"name\":\"Photo.pdf\", \"parent\":{\"id\":\"{{$node[\"Set\"].json[\"uploadFolder\"]}}\"}}"
            },
            {
              "name": "file",
              "value": "={{$node[\"HTTP Request\"].binary.data.fileName}}"
            }
          ]
        },
        "headerParametersUi": {
          "parameter": [
            {
              "name": "Authorization",
              "value": "Bearer <token>"
            }
          ]
        }
      },
      "name": "HTTP Request1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        478,
        414
      ],
      "credentials": {
        "httpHeaderAuth": "TokenVal"
      }
    }
  ],
  "connections": {}
}

Any help will be appreciated.

Hey @Kirst, welcome to the community.

As far as I know, sending data and the file at the same time as multipart/form-data it’s yet not possible. A workaround would be sending the file and then with the ID returned doing a put with the metadata ( name and parent ) assuming, of course, the API allows you to do so. To send the file check the example below.
If something is not clear simply get back to me.

{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "requestMethod": "POST",
        "url": "https://webhook.site/3786832a-7b75-43bb-a71f-3156a4c76526",
        "jsonParameters": true,
        "options": {
          "bodyContentType": "multipart-form-data"
        },
        "sendBinaryData": true,
        "binaryPropertyName": "file:data"
      },
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        890,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://i.ytimg.com/vi/MPV2METPeJU/maxresdefault.jpg",
        "responseFormat": "file",
        "options": {}
      },
      "name": "HTTP Request1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        540,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "HTTP Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request1": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Thanks @RicardoE105,

At least I know I’m not going mad. Yes, unfortunately at the moment that Endpoint requires the content and metadata to be sent in the same request. Would it be possible to do the call via a ‘Function’?

Nice idea @Kirst , just tested it and yes. Check the example below. Keep in mind that in order to require an NPM package on the function node you have to set an env variable. By default require a module is not allowed. In the example, I used the request-promise-native module and told N8N to use it like this export NODE_FUNCTION_ALLOW_BUILTIN=request-promise-native. More info about that here.

{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "const request = require('request-promise-native');\n\nconst binaryData = items[0].binary.data;\n\nconst metadata = {\n  name: binaryData.fileName,\n  parent: { id: 123 }\n}\n\nconst options = {\n  uri: 'https://internal.users.n8n.cloud/webhook-test/2/webhook/webhook',\n  headers: {\n    'content-type': 'multipart/form-data',\n  },\n  formData: {\n    file: {\n      value: Buffer.from(binaryData.data, 'base64'),\n      options: {\n\t    filename: binaryData.fileName,\n\t    contentType: binaryData.mimeType,\n\t  },\n \t},\n    attributes: JSON.stringify(metadata),\n  },\n  method: 'POST',\n}\n\nawait request(options);\n\nreturn [{ json: {} }]"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        670,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://i.insider.com/5df126b679d7570ad2044f3e?width=1100&format=jpeg&auto=webp",
        "responseFormat": "file",
        "options": {}
      },
      "name": "HTTP Request",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 1,
      "position": [
        470,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "HTTP Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request": {
      "main": [
        [
          {
            "node": "Function",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
3 Likes

Thank you very much @RicardoE105. The solution work great. I also tried to make it work using the axios library but that failed even the code works standalone outside of n8n. I will raise a separate topic on that.

1 Like

@Kirst Glad that it worked.

I won’t work with Axios because I’m guessing you used NODE_FUNCTION_ALLOW_BUILTIN, which is used for libraries that n8n uses and Axios it’s not one of them. If you want to use a module that it’s not used within n8n like Axios, you would have to use the environment variable NODE_FUNCTION_ALLOW_EXTERNAL=axios instead. More info about that here.

Thanks @RicardoE105

Yes I defined axios as follows:

 docker run -it --rm   --name n8n   -p 5678:5678 -e NODE_FUNCTION_ALLOW_EXTERNAL=axios,request-promise-native,form-data,fs,request  -v ~/.n8n:/root/.n8n   n8nio/n8n   n8n start --tunnel

It still fails though not with a complaint about axios, but in the sample code below, even the echo service does not reach the console.log for success or error.

const axios = require('axios');

(async () => {
  try {
    const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1')
    console.log(response.data.url);
    console.log(response.data.explanation);
    return [{ json: {'value':'success'} }] 
  } catch (error) {
    console.log(error.response.body);
    return [{ json: {'value':'error'} }] 
  }
})();

So whilst the function executes, there is no error raised or console.log. I’m trying to understand why it doesn’t execute the axios calls. Recreated it with this ‘request’ example as well, but similar behaviour.

var request = require('request');
var options = {
  'method': 'GET',
  'url': 'https://jsonplaceholder.typicode.com/todos/1',
  'headers': {
  }
};
await request(options, function (error, response) {
  if (error) throw new Error(error);
  console.log('Does not reach this part');
  console.log(response.body);
  return [{ json: {} }] 
});

Hey @Kirst, sorry for the late response.

Ok the first example, the one with axios, will never get to the console.log as you are calling an async function without the await keyword. The function is returning before the HTTP request responds.

With regard to the second example, it will not work either because the request library do not return a promise and the keyword await as far as I know works when the function you are calling return a promise. To make work you have to either require request-promise and request or just use
request-promise-native.

I created two examples, one with axios and one with request-promise-native so that you can see it’s login to the console.

Hope that helps, let me know if you have any further questions.

{
  "nodes": [
    {
      "parameters": {},
      "name": "Start",
      "type": "n8n-nodes-base.start",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "var request = require('request-promise-native')\n\nvar options = {\n  'method': 'GET',\n  'url': 'https://jsonplaceholder.typicode.com/todos/1',\n  'json': true,\n  'headers': {\n  }\n};\n\ntry {\n  const response = await request(options)\n    console.log(response);\n    return [{ json: response }] \n} catch (exception) {\n  throw new Error(exception);\n}\n\n\n"
      },
      "name": "Function",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        530,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "const axios = require('axios');\n\ntry {\n    const response = await axios.get('https://jsonplaceholder.typicode.com/todos/1')\n    console.log(response.data);\n    return [{ json:response.data }] \n  } catch (error) {\n    console.log(error.response.body);\n    return [{ json: {'value':'error'} }] \n  }\n"
      },
      "name": "Function1",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        770,
        300
      ]
    }
  ],
  "connections": {
    "Start": {
      "main": [
        [
          {
            "node": "Function",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Function": {
      "main": [
        [
          {
            "node": "Function1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Just FYI with [email protected] a Box Integration got added.