How to add a file to a component on entry creation?

In Strapi V4, assuming I have taken the file out of ctx.request.files and called it my_file, how do I append this file to a component when I create it on entry creation for example.

strapi.service("api::restaurant.restaurant").create({
  data: {
    name: "My Restaurant",
    menus: [{
      name: "Lunch Menu,
      menu_pdf: my_file,
    }]
  }
});

I know that if this were a top level field, I can just shove the file into a separate files object, but I don’t know how to do that for a component?

So I’ve solved this and I’m adding an example work through here so that if/when others find this page they know what to do.

So let’s assume you’ve posted a file to your Strapi backend

const form = new FormData();
form.append("my_file", file);
axios.post("/api/restaurant", form);

In your controller, you receive a ctx object, which contains the file in it’s request:

module.exports = async (ctx) => {
  const { my_file } = ctx.request.files;
}

To upload it and attach it to a top-level field (i.e. a field that is not in a component/dynamic zone), you can simply do

strapi.service("api::restaurant.restaurant").create({
  data: {
    // spread any other data
    ...ctx.request.body
  }
  files: {
    // use the top-level field name as your key, and the file as your value
    name_of_the_field: my_file
  }
});

To add a file to a component, whilst you create it, you need to upload it. I couldn’t find any docs on how upload works… So what I did was

// the file
const my_file;
const uploaded_file = strapi.service("plugin::upload.upload").upload(
  my_file,
  {
    alternativeText: "Some alt text",
    caption: "Some caption or empty string?",
    // This line is important for some reason... 
    // If you don't include it, your media library won't work
    mime: my_file.type,
  }
);
// this is a file object, and will have a uid that corresponds to a media library object
console.log(uploaded_file);
strapi.service("api::restaurant.restaurant").create({
  data: {
     // spread all your other data
    ...ctx.request.body,
    // create your component, same for repeatable or single (just no array on single)
    some_component: [{
       // Your other component data
      ...component_data,
      // Use the id from uploaded_file, in the field of your component that takes a file
      component_field_that_takes_a_file: uploaded_file.id
    }],
  }
});

Hope this helps anyone struggling to understand how add files programmatically.

2 Likes

Hello there. With the code below, only a record was created. No file was uploaded.
The POST request was sent with “multipart/form-data”.

schema.json

{
  "kind": "collectionType",
  "collectionName": "stock_check_uploads",
  "info": {
    "singularName": "stock-check-upload",
    "pluralName": "stock-check-uploads",
    "displayName": "StockCheckUpload"
  },
  "options": {
    "draftAndPublish": false
  },
  "pluginOptions": {},
  "attributes": {
    "User": {
      "type": "relation",
      "relation": "manyToOne",
      "target": "plugin::users-permissions.user",
      "inversedBy": "StockCheckUploads"
    },
    "File": {
      "allowedTypes": [
        "files"
      ],
      "type": "media",
      "multiple": false,
      "required": true
    }
  }
}

strapi/src/api/stock-check-upload/controllers/stock-check-upload.ts

import { factories } from '@strapi/strapi'

module.exports = factories.createCoreController('api::stock-check-upload.stock-check-upload', {
  async create(ctx) {
    const {csv_file} = ctx.request.files;
    const user_id = ctx.state.user.id;

    return await strapi.entityService.create('api::stock-check-upload.stock-check-upload', {
      data: {
        User: user_id,
        ...ctx.request.body,
      },
      files: {
        File: csv_file,
      },
    });
  },
});

It said:

[2023-09-07 05:45:21.447] error: Cannot set properties of undefined (setting 'tmpWorkingDirectory')
TypeError: Cannot set properties of undefined (setting 'tmpWorkingDirectory')

Thanks.

This works. Thanks.

import { factories } from '@strapi/strapi'

module.exports = factories.createCoreController('api::stock-check-upload.stock-check-upload', {
  async create(ctx) {
    const user_id = ctx.state.user.id;

    return await strapi.entityService.create('api::stock-check-upload.stock-check-upload', {
      data: {
        User: user_id,
        ...ctx.request.body,
      },
      files: {
        File: ctx.request.files['file_key_in_upload_form'],
      },
    });
  },
});