UXP: document saveAs method is not saving the document as a jpeg/png

,

Hi, we are trying to save the current document as jpeg/png in the temp folder and then read it as an ArrayBuffer for sending it on the API. Here is our code:

  const app = require("photoshop").app;
  const fs = await require("uxp").storage.localFileSystem;
  const formats = require("uxp").storage.formats;
  const currentDocument = app.activeDocument;
  const tempFolder = await fs.getTemporaryFolder();
  const file = await tempFolder.createFile("test.jpg", { overwrite: true });
  console.log(file, "fileToken");
  currentDocument.saveAs.jpg(
        file,
        {
          quality: 7,
        },
        true
      );
   const arrayBuffer = await file.read({ format: formats.binary });
   console.log(arrayBuffer, "arrayBuffer");

We have tried this without the temp folder too. Code for that:

  const app = require("photoshop").app;
  const fs = await require("uxp").storage.localFileSystem;
  const formats = require("uxp").storage.formats;
  const currentDocument = app.activeDocument;
  const file = await fs.getFileForSaving("test.jpg");
  console.log(file, "fileToken");
  currentDocument.saveAs.jpg(
        file,
        {
          quality: 7,
        },
        true
      );
   const arrayBuffer = await file.read({ format: formats.binary });
   console.log(arrayBuffer, "arrayBuffer");

But the issue is that the saveAs.jpg method doesn’t save the file at all. The file token is also generated correctly in both the cases. Just the saveAs method doesn’t create a file on the system.

Our manifest config:

 "version": "1.0.0",
  "main": "index.html",
  "manifestVersion": 4,
  "host": {
    "app": "PS",
    "minVersion": "23.0.0",
    "data": {
      "apiVersion": 2,
      "loadEvent": "use"
    }
  },
   "requiredPermissions": {
    "allowCodeGenerationFromStrings": true,
    "localFileSystem": "fullAccess"
  }

App versions that we are using:
Photoshop: 24.0
UXP dev tools: 1.7.0

Please let me know if you require any more details for the solution.
Thanks in advance.

Although I am not exactly sure what is causing the problem, try to pay attention to the following items.

  • use executeAsModal
  • Document.save.jpg() returns a promise, so add an “await” to it
const { app, core } = require('photoshop') ;
const { localFileSystem, formats } = require('uxp').storage ;

const readJpg = async () => {
  try {
    await core.executeAsModal(
      async (control) => {
        const tempFolder = await localFileSystem.getTemporaryFolder() ;
        const file = await tempFolder.createFile('test.jpg', {overwrite: true}) ;

        const currentDocument = app.activeDocument ;
        await currentDocument.saveAs.jpg(
          file,
          {
            quality: 7,
          },
          true
        ) ;

        const arrayBuffer = await file.read({format: formats.binary}) ;
        console.log(arrayBuffer, 'arrayBuffer') ;
      }, 

      {'commandName': 'readJpg'}
    ) ;
  } catch(e) {
    console.log(e) ;
  }
} ;

Thanks, we tried this but still facing the same issue.

What is the full absolute filepath of the file?

There is also new way to handle files. https://developer.adobe.com/photoshop/uxp/2022/uxp/reference-js/Modules/FileSystem/

(this works only in the most recent PS version I think)

1 Like

Now try setting manifestVersion to 5. requiredPermissions is supported from v5.

When I tried it, it may have worked stably after once I changed it to v5. After that, it also worked with v4.

"manifestVersion": 5,
"requiredPermissions": {
  "localFileSystem": "fullAccess"
}, 

We changed this but it still doesn’t work.
I don’t think it is permissions issue, because doing something like this

 const tempFolder = await localFileSystem.getTemporaryFolder();
 const file = await tempFolder.createFile("test.txt", { overwrite: true });
 file.write("qwerty");

creates a text file with qwerty as text.
It’s just the currentDocument.saveAs.png method that doesn’t create a file at all.

For temp folder approach, the nativePath is something like :
C:\Users\gunjan\AppData\Local\Temp\Adobe\UXP\PluginsStorage\PHSP\24\Developer\e26f73db\PluginData\test.png

For getFileForSaving approach, the nativePath is:
C:\Users\bangd\Documents\test.png

1 Like

Not sure how to save the document with these file instances. The save method that we are using (https://developer.adobe.com/photoshop/uxp/2022/ps_reference/classes/document/#saveas) requires a UXP File token (https://developer.adobe.com/photoshop/uxp/uxp/reference-js/Modules/uxp/Persistent%20File%20Storage/File/).

Do you know some other methods for saving the current document as a png/jpeg that can work with the file classes that you mentioned?

Thank you so much for helping.

Does that mean saveAs.jpg is working?

I guess saveAs.png needs to specify another save option (property ‘quality’ does not exist). The following code worked.

const readPng = async () => {
  try {
    await core.executeAsModal(
      async (control) => {
        const tempFolder = await localFileSystem.getTemporaryFolder() ;
        const file = await tempFolder.createFile('test.png', {overwrite: true}) ;

        const currentDocument = app.activeDocument ;
        await currentDocument.saveAs.png(
          file,
          {
            compression: 6,
          },
          true
        ) ;

        const arrayBuffer = await file.read({format: formats.binary}) ;
        console.log(arrayBuffer, 'arrayBuffer') ;
      }, 

      {'commandName': 'readPng'}
    ) ;
  } catch(e) {
    console.log(e) ;
  }
} ;
1 Like

Ok, so it is working for vanilla javascript based plugin and we are working with plugin in react. Not sure why it’s not working in react.

Do you have any input?

Trying to asynchronize functions that cannot be async, not adding await where it should be added, etc. These are the paths I have taken in the past.

However, I cannot say for sure without seeing your code.

Hi, exactly the the same issue over here. Did you finally solve it? @gunjan Thanks a lot!

I implemented save as.
I was able to get help from this blog.

First, change the manifestVersion to 5
Please add requiredPermissions at the bottom.

{
  "id": 
  "name": 
  "version": 
  "main": 
  "host": {
    "app": 
    "minVersion": 
  },
  "manifestVersion": 5,
  "entrypoints": [
  ],
  "icons": [
  ],
    "requiredPermissions": {
    "localFileSystem": "fullAccess"
  }
}
  1. Record the behavior in Action and convert it to js code.

If you convert it, it will look like this, but if you run it as is, it won’t work. A few modifications need to be made.

async function actionCommands() {
    let command;
    let result;
    let psAction = require("photoshop").action;

    // Save
    command = {"_obj":"save","as":{"_obj":"PNGFormat","embedIccProfileLastState":{"_enum":"embedOff","_value":"embedOff"},"method":{"_enum":"PNGMethod","_value":"thorough"}},"documentID":421,"embedProfiles":false,"in":{"_kind":"local","_path":"C:\\Users\\userName\\Desktop\\dfddfdfdfdf123.png"},"lowerCase":true};
    result = await psAction.batchPlay([command], {});
}

async function runModalFunction() {
    await require("photoshop").core.executeAsModal(actionCommands, {"commandName": "Action Commands"});
}

runModalFunction();

If you modify and run the code in token form as shown below, the newly created ps file will be saved as png.

// save as new File
async function exportJS() {
    let path = "file:C:\\Users\\userName\\Desktop\\"
    // console.log(path);
    let file = await fs.getEntryWithUrl(path);
    // console.log(file);
    let token = await fs.createSessionToken(file);
    // console.log(token);
 
    let result;
    let psAction = require("photoshop").action;
 
    let command = [
        // Save
        {"_obj":"save","as":{"_obj":"PNGFormat","embedIccProfileLastState":{"_enum":"embedOff","_value":"embedOff"},"method":{"_enum":"PNGMethod","_value":"thorough"}},"embedProfiles":false,"in":{"_kind":"local","_path":token},"lowerCase":true}
    ];
    result = await psAction.batchPlay(command, {});
 }
 
 async function runModalFunction() {
    await require("photoshop").core.executeAsModal(exportJS, {"commandName": "Action Commands"});
 }
 
 runModalFunction();

I tried entering the new file name + png in path, but the code didn’t work.
I would like to change the file name when saving a new file. If anyone knows, please leave a further reply.

// save as new File
async function exportJS() {
    let path = "file:C:\\Users\\userName\\Desktop\\" + "test123.png"
    // console.log(path);
    let file = await fs.getEntryWithUrl(path);
    // console.log(file);
    let token = await fs.createSessionToken(file);
    // console.log(token);
 
    let result;
    let psAction = require("photoshop").action;
 
    let command = [
        // Save
        {"_obj":"save","as":{"_obj":"PNGFormat","embedIccProfileLastState":{"_enum":"embedOff","_value":"embedOff"},"method":{"_enum":"PNGMethod","_value":"thorough"}},"embedProfiles":false,"in":{"_kind":"local","_path":token},"lowerCase":true}
    ];
    result = await psAction.batchPlay(command, {});
 }
 
 async function runModalFunction() {
    await require("photoshop").core.executeAsModal(exportJS, {"commandName": "Action Commands"});
 }
 
 runModalFunction();

png files already saved on the PC worked well if I added app.activeDocument.path to the path part.

   async function exportJS() {
      let path = "file:"+ app.activeDocument.path
      // let fs = uxp.storage.localFileSystem;
      let file = await fs.getEntryWithUrl(path);
      let token = await fs.createSessionToken(file);
      
      let result;
      let psAction = require("photoshop").action;

      let command = [
         // Save
         {"_obj":"save","as":{"_obj":"PNGFormat","embedIccProfileLastState":{"_enum":"embedOff","_value":"embedOff"},"method":{"_enum":"PNGMethod","_value":"thorough"}},"embedProfiles":false,"in":{"_kind":"local","_path":token},"lowerCase":true}
      ];
      result = await psAction.batchPlay(command, {});
   }

   async function runModalFunction() {
      await require("photoshop").core.executeAsModal(exportJS, {"commandName": "Action Commands"});
   }

 runModalFunction();

for answering your question, there are some methods for accessing local files.
getEntryWithUrl method for files which are already exist as same file name.(just overwrite it)
regarding that saving as a modified file name, you need to use different method that called createEntryWithUrl. this creates file Entry to generate new file path.
so overall this is the code to create new file path.

const uxpfs = require("uxp").storage.localFileSystem;
const os = require("os");

//desktop path
const desktopPath = path.join(`file:${os.homedir()}`, "Desktop");
async function modifyPng() {
    await require("photoshop").core.executeAsModal(async () => {
      try {
        /*
       put together desktop path and new file name.
       and for adapting Mac and Win, I use join method.
       */
        const newFile = path.join(desktopPath, "test111.png");
        /*
             create entry object for new file name.
             overwrite is optional. if you want to overwrite a file if it already exists.
        */
        const newToken = await uxpfs.createSessionToken(
          await uxpfs.createEntryWithUrl(newFile, { overwrite: true })
        );

        let result;
        let psAction = require("photoshop").action;

        let command = [
            // Save
            {"_obj":"save","as":
            {"_obj":"PNGFormat","embedIccProfileLastState":{"_enum":"embedOff","_value":"embedOff"},
            "method":{"_enum":"PNGMethod","_value":"thorough"}},
            "embedProfiles":false,"in":{"_kind":"local","_path": newToken},"lowerCase":true}
        ];
        result = await psAction.batchPlay(command, {});
        console.log(result);
      } catch (e) {
        console.error(e);
      }
    }, {"commandName": "Action Commands"});
 }

https://developer.adobe.com/photoshop/uxp/2022/uxp-api/reference-js/Modules/uxp/Persistent%20File%20Storage/FileSystemProvider/#createentrywithurlurl-options

and I won’t recommend use path variable.
On UXP system, there is a global object path that almost same type of path module on Node.js.

https://developer.adobe.com/photoshop/uxp/2022/uxp-api/reference-js/Global%20Members/Path/

That why using like this.

let path = "something"

this pollutes global scope.

and also fs variable name can easy to make you confused fs module and uxp.storage.localFileSystem. I recommend carefully name them. Even you can see examples on document.
And any modules fs or os are Node like modules but they are slightly different from Node methods.

1 Like

I did not understand what was related to the path when I first looked at the official document,
but I learned it after running the code you wrote.
Thank you for sharing your knowledge.