Custom controller upload files on entry creation with Stripe

I’m having trouble with my custom controller when creating a new entry, uploading files, and then redirecting to a Stripe payment page. I’m able to create the entry with just the data and go to the payment page but have trouble understanding how to upload the files too. I’ve searched here, the documentation, YouTube, but I’m struggling to find both entry creation with file upload. Any help would be greatly appreciated!

Controller:

'use strict';

/**
 *  listing controller
 */

const { createCoreController } = require('@strapi/strapi').factories;
const stripe = require('stripe')(process.env.STRIPE_SK);

module.exports = createCoreController('api::listing.listing', ({ strapi }) =>  ({
    async create(ctx) {

        const listing = JSON.parse(ctx.request.body.data);
        const files = ctx.request.files;
        console.log(listing, files)
    
        const BASE_URL = ctx.request.headers.origin || 'http://localhost:3000'

        const session = await stripe.checkout.sessions.create({
            line_items: [
                {
                    price_data: {
                        currency: 'usd',
                        product_data: {
                            name: 'Regular Listing'
                        },
                        unit_amount: 150 * 100
                    },
                    quantity: 1
                },
            ],
            mode: 'payment',
            success_url: `${BASE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
            cancel_url: BASE_URL,
        })
        console.log(session)

        // add listing to the database
        const entity = await strapi.service('api::listing.listing').create({
            data: {
                contact_firstname: listing.contact_firstname,
                contact_lastname: listing.contact_lastname,
                contact_phone: listing.contact_phone,
                contact_email: listing.contact_email,
                contact_city: listing.contact_city,
                contact_state: listing.contact_state,
                title: listing.title,
                price: listing.price,
                description: listing.description,
                category: listing.category,
                featured: listing.featured,
                publishedAt: null,
                session_id: session.id,
            },
            files: { files }
        });
        console.log(entity)

        return { id: session.id, url: session.url }
    },
  
  }));

File console log:

{
  'files.images': File {
    _events: [Object: null prototype] {},
    _eventsCount: 0,
    _maxListeners: undefined,
    size: 456045,
    path: '/var/folders/x2/hn2mjh_15r523mwh23s0c7ym0000gn/T/upload_ccb49824cd7a8295e3d5de5572ae0e30',
    name: 'image01.jpeg',
    type: 'image/jpeg',
    hash: null,
    lastModifiedDate: 2022-04-01T22:41:25.447Z,
    _writeStream: WriteStream {
      fd: null,
      path: '/var/folders/x2/hn2mjh_15r523mwh23s0c7ym0000gn/T/upload_ccb49824cd7a8295e3d5de5572ae0e30',
      flags: 'w',
      mode: 438,
      start: undefined,
      pos: undefined,
      bytesWritten: 456045,
      closed: true,
      _writableState: [WritableState],
      _events: [Object: null prototype] {},
      _eventsCount: 0,
      _maxListeners: undefined,
      [Symbol(kFs)]: [Object],
      [Symbol(kIsPerformingIO)]: false,
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false
  }
System Information
  • Strapi Version: 4.1.7
  • Operating System: macOS Monterey 12.2.1
  • Database: PostgreSQL
  • Node Version: 16.14.0
  • NPM Version: 8.3.1

Maybe you can print the origin create function to check how thee upload file.
put console.log(super.create.toString()) in the first line.
If there is no upload file in “api::listing.listing”, console.log(strapi) to find some infomation.

Here’s the log from super.create.toString()

async create(ctx) {
      const { query } = ctx.request;

      const { data, files } = parseBody(ctx);

      if (!isObject(data)) {
        throw new ValidationError('Missing "data" payload in the request body');
      }

      const sanitizedInputData = await this.sanitizeInput(data, ctx);

      const entity = await strapi
        .service(uid)
        .create({ ...query, data: sanitizedInputData, files });
      const sanitizedEntity = await this.sanitizeOutput(entity, ctx);

      return this.transformResponse(sanitizedEntity);
    }

So I’m able to upload the file(s) now but they’re not connected with the entry

I found the issue :face_with_symbols_over_mouth: I was naming the file FormDate wrong. It needed to match my field name.

Client-side solution:

formData.append(`images`, file, file.name);

Controller:

'use strict';

/**
 *  listing controller
 */

const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::listing.listing', ({ strapi }) =>  ({
    async create(ctx) {

        const listing = JSON.parse(ctx.request.body.data);
        const files = ctx.request.files;
        console.log(listing, files)
    

        // add listing to the database
        const entity = await strapi.service('api::listing.listing').create({
            data: {
                contact_firstname: listing.contact_firstname,
                contact_lastname: listing.contact_lastname,
                contact_phone: listing.contact_phone,
                contact_email: listing.contact_email,
                contact_city: listing.contact_city,
                contact_state: listing.contact_state,
                title: listing.title,
                price: listing.price,
                description: listing.description,
                category: listing.category,
                featured: listing.featured,
                publishedAt: null,
                session_id: session.id,
            },
            files
        });
        console.log(entity)

        const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
  
        return { id: session.id, sanitizedEntity }
    },
  
  }));
2 Likes

Curious where your files are being saved to in this:

const entity = await strapi.service('api::listing.listing').create({
            data: {
                contact_firstname: listing.contact_firstname,
                contact_lastname: listing.contact_lastname,
                contact_phone: listing.contact_phone,
                contact_email: listing.contact_email,
                contact_city: listing.contact_city,
                contact_state: listing.contact_state,
                title: listing.title,
                price: listing.price,
                description: listing.description,
                category: listing.category,
                featured: listing.featured,
                publishedAt: null,
                session_id: session.id,
            },
            files
        });

Are they being added to the table?

I’m struggling with saving files to somewhere other than public/. :confused:

The files are saved to an Amazon S3 bucket using the AWS S3 plugin.

Here are the providers docs:

Strapi has broken file upload in 4.20.4 . ctx.request.files no long has the file in it. in 4.15.0 this works.