Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to send Buffer or base64 image via entity service or upload plugin #15524

Open
cpaczek opened this issue Jan 23, 2023 · 6 comments · May be fixed by #15755
Open

Unable to send Buffer or base64 image via entity service or upload plugin #15524

cpaczek opened this issue Jan 23, 2023 · 6 comments · May be fixed by #15755
Labels
issue: enhancement Issue suggesting an enhancement to an existing feature source: core:upload Source is core/upload package

Comments

@cpaczek
Copy link
Collaborator

cpaczek commented Jan 23, 2023

Bug report

Required System information

Node: v18
Strapi: 4.5.6
OS: Windows 10]
DB: SQLite

Describe the bug

There is not a way afaik to send a buffer image or a base 64 image using the Entity Service API or the upload plugin. There is this forum post but it seems like a janky way to do this and I couldn't get it working: https://forum.strapi.io/t/upload-buffer-using-strapi-upload/18807

Steps to reproduce the behavior

Create new strapi app.
Try an upload a buffer using the entity service API.

Expected behavior

Ability to pass buffer or similar node js datatype within entity service

Code snippets

            await strapi.entityService.create('plugin::og-generator.og-image', {
                populate: '*',
                data:{
                    collection: collectionName,
                    collectionID: id
                },
                files:{
                    media: image
                }
            })
        }catch(err){
            console.log(err)
        }```
@cpaczek
Copy link
Collaborator Author

cpaczek commented Jan 23, 2023

   uploadFileBinary: async function (file, uuid) {
        const bytesToKbytes = (bytes) => Math.round((bytes / 1000) * 100) / 100;
        const config = strapi.config.get('plugin.upload');

        const createAndAssignTmpWorkingDirectoryToFiles = () => fse.mkdtemp(path.join(os.tmpdir(), 'strapi-upload-'));

        const fileNameNoExt = path.basename(uuid, "jpeg");
        const entity = {
            name: `test ${fileNameNoExt}`,
            hash: `${uuid}_${fileNameNoExt}`,
            ext: "jpeg",
            mime: "image/jpeg",
            size: bytesToKbytes(Number(file.length)),
            provider: config.provider,
            folder: 2,
            tmpWorkingDirectory: await createAndAssignTmpWorkingDirectoryToFiles(),
            // getStream: () => Readable.from(file),
            getStream: () => { return new stream.Readable.from(file)}
        };

        await strapi.plugin('upload').service('upload').uploadImage(entity)

        return strapi
            .query('plugin::upload.file')
            .create({ data: entity });

    },
    ```
    Seems like this is the only way, probably going to make a PR to put this functionality into core.

@derrickmehaffy derrickmehaffy added issue: bug Issue reporting a bug severity: medium If it breaks the basic use of the product but can be worked around status: confirmed Confirmed by a Strapi Team member or multiple community members source: core:upload Source is core/upload package labels Feb 2, 2023
@joshuaellis
Copy link
Member

Hey @cpaczek, after talking with the rest of the content squad, this is actually an enhancement request because we do not support it currently – can appreciate the desire to do so of course! It's also a duplicate of #5297 so i'm going to close this issue in favour of the former, thanks again for contributing to the project :)

@joshuaellis joshuaellis closed this as not planned Won't fix, can't repro, duplicate, stale Feb 8, 2023
@joshuaellis joshuaellis added status: duplicate Is a duplicate of another issue and removed issue: bug Issue reporting a bug severity: medium If it breaks the basic use of the product but can be worked around status: confirmed Confirmed by a Strapi Team member or multiple community members labels Feb 8, 2023
@petersg83 petersg83 added issue: enhancement Issue suggesting an enhancement to an existing feature and removed status: duplicate Is a duplicate of another issue labels Feb 15, 2023
@petersg83
Copy link
Contributor

Reopening based on the discussion there: #5297 (comment) :)

@petersg83 petersg83 reopened this Feb 15, 2023
@joshuaellis joshuaellis linked a pull request Feb 15, 2023 that will close this issue
@Kocal
Copy link

Kocal commented Aug 10, 2023

Thanks @cpaczek :)

Here is an adapted version of his function, that you can easily put inside a service definition:

import utils from "@strapi/utils";
import os from "os";
import * as fse from "fs-extra";
import * as stream from "stream";
import path from "path";
import axios from "axios";

export default ({ strapi }: { strapi: Strapi }) => {
  /**
   * Asynchronously upload a buffer to the media library.
   * @see https://github.com/strapi/strapi/pull/15755
   *
   * @param {Object} params - The parameters for uploading the buffer.
   * @param {Buffer} params.file - The buffer to be uploaded.
   * @param {string} params.fileName - The name of the file to be uploaded.
   * @param {number} [params.folder] - The folder to upload the file to.
   * @param {string} params.mime - The MIME type of the file.
   * @param {string} params.ext - The file extension.
   * @param {string} [params.alternativeText] - The alternative text for the file.
   * @param {string} [params.caption] - The caption for the file.
   *
   * @throws {utils.errors.ApplicationError} If `mime`, `ext`, `file`, or `fileName` is undefined.
   *
   * @returns {Promise<Object>} The uploaded file entity.
   */
  async function uploadFileBuffer({ file, fileName, folder, mime, ext, alternativeText, caption }) {
    if (!mime) {
      throw new utils.errors.ApplicationError("mime type is undefined");
    }
    if (!ext) {
      throw new utils.Errors.ApplicationError("ext is undefined");
    }
    if (!file) {
      throw new utils.Errors.ApplicationError("file is undefined");
    }
    if (!fileName) {
      throw new utils.Errors.ApplicationError("fileName is undefined");
    }

    const config = strapi.config.get("plugin.upload");
    const uploadService = strapi.services["plugin::upload.upload"];

    const randomSuffix = () => crypto.randomBytes(5).toString("hex");
    const generateFileName = (name) => {
      const baseName = utils.nameToSlug(name, { separator: "_", lowercase: false });

      return `${baseName}_${randomSuffix()}`;
    };
    const createAndAssignTmpWorkingDirectoryToFiles = async (files) => {
      const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), "strapi-upload-"));

      if (Array.isArray(files)) {
        files.forEach((file) => {
          file.tmpWorkingDirectory = tmpWorkingDirectory;
        });
      } else {
        files.tmpWorkingDirectory = tmpWorkingDirectory;
      }

      return tmpWorkingDirectory;
    };

    const entity = {
      name: `${fileName}${ext}`,
      hash: generateFileName(fileName),
      ext,
      mime,
      size: utils.file.bytesToKbytes(Number(file.length)),
      provider: config.provider,
      folder,
      caption,
      alternativeText,
      tmpWorkingDirectory: await createAndAssignTmpWorkingDirectoryToFiles({}),
      getStream() {
        return stream.Readable.from(file);
      },
    };

    await uploadService.uploadImage(entity);

    return strapi.query("plugin::upload.file").create({ data: entity });
  }

  return {
    async myServiceFunction() {
      const url = `https://example.com/my-image.jpg`;
      const response = await axios.get(url, {
        responseType: "arraybuffer",
      });
      const fileParts = path.parse("my-image.jpg");
      const uploadedFile = await uploadFileBuffer({
        file: response.data,
        fileName: fileParts.name,
        mime: response.headers["content-type"],
        ext: fileParts.ext,
        alternativeText: `The alternative text...`,
        caption: `The caption`,
        folder: '/',
      });
      console.log(uploadedFile);
    },
  };
};

@Boegie19
Copy link
Collaborator

@joshuaellis Anyway how we can get a PR like this accepted? since it is needed see for example strapi/documentation#1855

@strapi-bot
Copy link

This issue has been mentioned on Strapi Community Forum. There might be relevant details there:

https://forum.strapi.io/t/upload-buffer-using-strapi-upload/18807/9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue: enhancement Issue suggesting an enhancement to an existing feature source: core:upload Source is core/upload package
Projects
Status: PR Open
Status: To Review
Status: To review
Development

Successfully merging a pull request may close this issue.

7 participants