2

I am trying to create and publish an image as an asset using the Contentful Content Management API and have been unsuccessful so far, I have been able to successfully create and publish an entry and I cannot figure out why asset is not working. Here is my code for creating an asset. The eventual goal is to make this work for an array of images/attachments but I am trying to get it working with just one before I handle multiple.

  makeAsset = async (item) => {
    try {
      var MClient = createClient({
        accessToken: process.env.REACT_APP_CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
      });
      let space = await MClient.getSpace(
        process.env.REACT_APP_CONTENTFUL_API_SPACE
      );
      let environment = await space.getEnvironment(
        process.env.REACT_APP_CONTENTFUL_ENVIRONMENT
      );

      const asset = await environment.createAsset({
        fields: {
          title: {
            "en-US": item.imgNames[0],
          },
          file: {
            "en-US": {
              fileName: item.images[0].name,
              contentType: item.images[0].type,
              uploadFrom: item.imgUrls[0],
            },
          },
        },
      });

      const processedAsset = await asset.processForAllLocales();
      processedAsset.publish();

And the error in the console is this:

ValidationFailed: {
  "status": 422,
  "statusText": "",
  "message": "Validation error",
  "details": {
    "errors": [
      {
        "name": "type",
        "value": "data:image/png;base64,iVBORw0KGg(The rest of the image URL from above)
        "type": "Object",
            "details": "The type of \"value\" is incorrect, expected type: Object",
            "path": [
            "fields",
            "file",
            "en-US",
            "uploadFrom"
            ]
         }
       ]
     },
"request": {
    "url": "assets",
    "headers": {
      "Accept": "application/json, text/plain, */*",
      "Content-Type": "application/vnd.contentful.management.v1+json",
      "X-Contentful-User-Agent": "sdk contentful-management.js/5.26.1; platform browser; os Windows;",
      "Authorization": "Bearer ...7ijJE"
    },
    "method": "post",
    "payloadData": "{\"fields\":{\"title\":{\"en-US\":\"scuba_mask\"},\"file\":{\"en-US\":{\"fileName\":\"scuba_mask.png\",\"contentType\":\"image/png\",\"uploadFrom\":\"data:image/png;base64,iVBORw0KG(The rest of image URL)

I have been trying to search for other example because the documentation is patchy and none of the examples are working for me. One solution I tried was using the fs.createReadStream() function but that was removed from React.

halfer
  • 19,824
  • 17
  • 99
  • 186

2 Answers2

3

@stefan judis, I used the code from the example and one of the functions is being deprecated in newer versions so I figured I would go out on a limb and replace something and it worked. For future reference for anyone else with this issue here is the solution that fixed my problem:

makeAsset = async (item) => {
try {
  //Contentful API Call
  var MClient = createClient({
    //Fetch access token from environment variables
    accessToken: process.env.REACT_APP_CONTENTFUL_MANAGEMENT_ACCESS_TOKEN,
  });
  //API call that requests the specified space
  let space = await MClient.getSpace(
    process.env.REACT_APP_CONTENTFUL_API_SPACE
  );
  let environment = await space.getEnvironment(
    process.env.REACT_APP_CONTENTFUL_ENVIRONMENT
  );

  const file = item.images[0];
  const contentType = file.type;
  const fileName = file.name;
  let asset = await environment.createAssetFromFiles({
    fields: {
      title: {
        "en-US": fileName,
      },
      file: {
        "en-US": {
          contentType,
          fileName,
          file,
        },
      },
    },
  });
  asset = await asset.processForAllLocales();
  asset.publish();
} catch(error) {
  console.log(error);
}
}
  • @themetzmeir Hi I was wondering if you happen to know how to have this tied to an entry, belonging to another piece of data? It seems like the docs are showing how to upload an image but not how to connect to it to an entry. – justkeithcarr Aug 01 '21 at 19:39
  • 1
    @justkeithcarr you have to use environment.getEntry(EntryIDHere) and then convert the return into an object using .toPlainObject(); and then append your new data to the local object. Then you can execute the code above to update the object in contentful with the json characteristics of the local object. – themetzmeier Aug 02 '21 at 21:58
  • @themetzmeir thanks! I appreciate it. One more question if you don't mind, which function above would "update the object in contentful". – justkeithcarr Aug 05 '21 at 00:57
  • Seems like there isn't a common way of posting data along with an image at the same time. Like posting a blog text along with its image(s). Instead we have to make a post request first, and then update the entry with an image. If I'm not mistaken. – justkeithcarr Aug 05 '21 at 01:01
  • 1
    @justkeithcarr you would use the sdk method getEntryFromData() as follows: let updatedEntry = environment.getEntryFromData(UpdatedLocalObjectHere) followed by updatedEntry.update().then((updatedObject) => { updatedObject.publish() }); – themetzmeier Aug 06 '21 at 13:04
  • Thanks metzmeier. I might of asked the wrong question. I meant to ask if we're able to make an entry at the same time of creating asset/image for the entry. I saw a possible answer to that here https://stackoverflow.com/questions/42671080/contentful-api-upload-and-assign-image-to-an-entry, but it assumes we're able to get the entry id for each entry. I'm digging into it. Sorry about that, I see my question came off as if I need to update an entry. – justkeithcarr Aug 06 '21 at 20:50
  • 1
    @justkeithcarr, you most definitely can, you just have to use async/await to await the return of the published asset before you can reference it on the entry. You can do it all in the same .then() statement. – themetzmeier Aug 08 '21 at 03:06
  • Hi @themetzmeir you wouldn't happend to have this as a non-private repository on github? I'm wondering about the object you passed in your 'makeAsset' function. It looks like it's from a local object you made. – justkeithcarr Sep 12 '21 at 17:32
2

Heyooo, Contentful Developer Relations here.

You're right, the docs can be improved on that end. :/ Did you give createAssetFromFiles are try?

We have two different upload mechanisms.

  1. Provide a URL that Contentful will fetch to create an asset. In the JavaScript CMA SDK that's the createAsset method.

  2. Trigger a direct file upload. For that, we provide createAssetFromFiles.

We also have this a little bit older but working example in react. It uses createAssetFromFiles and uploads files right from the browser using the Content Management API. :)

I hope that helps, and sorry about the confusion.

stefan judis
  • 3,416
  • 14
  • 22