UXP plugin Upload active document using xhr to presigned URL

I am trying to upload the app.activeDocument to a presigned URL via XMLHttpRequest or Fetch but have not been successful in many attempts. I keep getting errors stating “unsupported body type” or “unsupported BodyInit type”. Is there another way to include the app.activeDocument into this call from a Photoshop UXP plugin? Any other methods I could try for uploading the image?

XHR

var docRef = app.activeDocument;
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener(“readystatechange”, function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open(“PUT”, preSignedUrl);
xhr.setRequestHeader(“Content-Type”, “image/jpeg”);
xhr.send(docRef);

Fetch

var docRef = app.activeDocument;
var myHeaders = new Headers();
myHeaders.append(“Content-Type”, “image/jpeg”);
var requestOptions = {
method: ‘PUT’,
headers: myHeaders,
body: docRef,
redirect: ‘follow’
};
fetch(preSignedUrl, requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log(‘error’, error));

But app.activeDocument isn’t an image. You have to export it as a JPEG and then upload it. Not sure what steps these would be exactly, but app.activeDocument is an object - that’s why you get errors when try to upload it as a JPEG

That was my initial thought as well and I tried to access the active document file from FileSystemProvider but I was not able to actually pick the file

See Document#saveAs documentation for information on exporting the document to a JPG.

I’ve tried using let fileEntry = require(‘uxp’).storage.localFileSystem.getFileForOpening(), but get the Error: Plugin is not permitted to access the file picker.

I also tried using the method you suggested from this post Saving a JPG to a selected folder - #3 by 4t0m1c
I found I had to use executeAsModal but still can’t get it to save to a temporary folder.

const lfs = uxp.storage.localFileSystem;
const tempFolder = await lfs.getTemporaryFolder();
const saveFileToFolder = async (tempFolder, fileName="temp")  => {
          const file = await tempFolder.createFile(`${fileName}.jpg`);
          const activeDocument = require("photoshop").app.activeDocument;
          return activeDocument.save(file);
      }
        
        await require('photoshop').core.executeAsModal(saveFileToFolder);

It errors saying the createFile is not a function.

I just need a way to attach the active document’s file object to the fetch or xhr upload.

You’re shadowing the global tempFolder in scope by adding it as an argument to saveFileToFolder. Your code is expecting Ps to pass tempFolder as an argument, but executeAsModal won’t do that (and is instead passing some other context, IIRC, so it’s definitely not what you’re expecting here.)

Instead, do:

await require("photoshop").core.executeAsModal(() => saveFileToFolder(tempFolder));
1 Like

That still did not seem to work in saving a temp file.
I still get the “Error: Plugin is not permitted to access the file picker. Manifest entry not found” when trying to point to an existing file as a source to upload to the presignedURL.

const lfs = require('uxp').storage.localFileSystem;
const folder = await lfs.getFolder();

This allows me to pick a folder but,

await lfs.getFileForOpening();
await lfs.getFileForSaving();

gives me the error above.

Is there any other method I can use to upload an image using the xhr call above?

To allow this access under v5, you will need to add this to your manifest:

"requiredPermissions": {
    "localFileSystem": "request"
}

Note that you can’t distribute plugins using manifest version 5 yet – the installer won’t be aware of this until April. So if you switch to manifest version 4, you won’t see this error either.

1 Like

Thank you. Adding those permissions allowed me to use the localFileSystem to point to the file I want to upload, but I still have been unsuccessful in executing the upload of the file via XHR or Fetch PUT calls to the presigned URL. This is the first panel that I have used API calls in. I have been able to read and write metadata to existing assets with XHR calls. I have also been successful in creating an asset with the following code:


      var data = JSON.stringify({
        "filename": "test.jpg",
        "metadata": [
          {
            "metadataDefinitionId": idOne,
            "metadataDefinitionValue": valueOne
          },
          {
            "metadataDefinitionId": idTwo,
            "metadataDefinitionValue": {
              "valueId": valueTwo
            }
          } 
        ],
        "metadataTemplateId": tempId,
        "securityTemplateIds": [
          securityId
        ]
      });
      
      var xhr = new XMLHttpRequest();
      xhr.withCredentials = true;
      
      xhr.addEventListener("readystatechange", function() {
        if(this.readyState === 4) {
          console.log(this.responseText);
        }
      });
      
      xhr.open("POST", myUrl);
      xhr.setRequestHeader("X-API-Key", keyContents);
      xhr.setRequestHeader("Authorization", auth);
      xhr.setRequestHeader("AccessToken", access);
      xhr.setRequestHeader("Content-Type", "application/json");
      
      xhr.send(data);

This XHR returns the presigned URL to upload the image to. I have attempted to use the PUT call to then upload to that presigned URL with the code below:

exportFile();
      async function exportFile() { 

        const uxp = require('uxp');  
        const lfs = uxp.storage.localFileSystem;  
        let uploadFile = await lfs.getFileForOpening();
        console.log(uploadFile);
        try{

          var xhr = new XMLHttpRequest();
          xhr.withCredentials = true;

          xhr.addEventListener("readystatechange", function() {
            if(this.readyState === 4) {
              console.log(this.responseText);
            }
          });

          xhr.open("PUT", preSignedUrl);
          xhr.setRequestHeader("Content-Type", "image/jpeg");   
          xhr.send(uploadFile);
        }
        catch(e){console.log(e);}
}

This errors with “unsupported body type”
I’ve also tried installing Axios and using this code:

var axios = require('axios');

          var config = {
            method: 'put',
            url: preSignedUrl,
            headers: { 
              'Content-Type': 'image/jpeg'
            },
            data : uploadFile
          };

          axios(config)
          .then(function (response) {
            console.log(response);
          })
          .catch(function (error) {
            console.log(error);
          });

This does not error but the response.data is “”, and the image still doesn’t upload.
Are there any API calls that could work to attach the image to the presigned URL?
A colleague was able to upload to this presigned URL with applescript and a curl command, but I am trying to do it from the UXP panel.

do shell script "curl -v --upload-file '" & filePath & "' '" & signedURL & "'"

Finally got this to work. I had to convert the uxp file object into binary to upload using the axios API call

let formats = require('uxp').storage.formats;
var binaryFile = await uploadFile.read({format: formats.binary});

Is it possible to distribute plugins using manifest version 5 now?