Exporting files


I am once again struggling with an issue. This time it is in regards to the export of files. I’ve looked around the Photoshop API as well as here on the forums and found two ways people can export files to my knowledge. This one is from the Photoshop API, Document section:

let entry = await require('uxp').storage.localFileSystem.getFileForSaving("target.psd");

// Save as a Copy (High JPG quality)
document.saveAs.jpg(entryJpg, { quality: 12 }, true);

// Save a PSB, with some options:
document.saveAs.psb(entryPsb, { embedColorProfile: true });

After looking through this forum though, I’ve never seen people use this. Everyone instead uses the batchPlay for export. Anyways, I am trying first this from the photoshop API but am running into an issue. Normally, as you can see in the code above, they use a file dialog to get the final path which is an entry. Then they use it to save it using the function. I am struggling trying to get a path to be converted into the entry. I don’t use a file dialog, I get it by combining the folder and the filename later on. I run into error where it mentions that it could not find an entry after I give it my path.

could not find an entry of: file:///...

Currently, I have this function which should give me back an entry based on the file path that I give it. I already checked but my manifest is not version 4, but 5, which was the most common issue on the forum for this problem.

async function pathToEntry(filePath) {
    const fs = require('uxp').storage.localFileSystem;
    try {
        const file = await fs.getEntryWithUrl(filePath);
        return file;
    } catch (e) {
        await showAlert(e);
        await showAlert(filePath);

My main questions are a) what is the main problem and b) should I use batchPlay instead since everyone else seemed to use it? If you need any more code from me, just say so! Thanks in advance!

You’re totally doing the right thing by using the API. There’s a lot of recipes out on the forum from people who are using Alchemist (or some other method) to generate batchPlay code. There are some good reasons for using batchPlay for exporting, for example if the file format is not exposed in the API (e.g. TIFF) or some parameters/settings from the export are unavailable. That said, I would recommend sticking to the API as much as possible, especially when exporting vanilla PSD/PSB or JPGs.

Now to the problem at hand: The function getEntryWithUrl expects that file to exist on disk (which obviously is not the case if you newly create it). In your case you should use that function to get the entry for the output folder (the parent folder of the export file). Then use the ‘createFile’ function on that folder to get a new entry to save to. So, something like this:

// If you don't have a handle for the export folder already, get it:
const folderEntry = await fs.getEntryWithUrl(folderPath);

// Create file handle for export
const fileEntry = await folderEntry.createFile(fileName, {overwrite: true});

// Export:
await document.saveAs.psd(fileEntry);

The code you show here doesn’t seem to work. It doesn’t show any errors either so it’s hard to troubleshoot! Looking through the API, it mentions a write method to write the data in. As stated here:

  • If the file does not exist yet, creates a File object but does not create the file on disk yet. You can then use write to create the file and give it content.

Now, I am guessing “saveAs” method works similarly. Could it be that after finishing writing the file data, I need to close the output somehow, similarly to how some other coding languages do it?

When I write “something like this”, I mean “like” :wink:. What I wrote was pseudocode. :slight_smile: I didn’t try it out verbatim. That said, principle is still the same and I stick with it. Get the entry to the folder, create a file in the folder (by that I mean the file handle), pass that handle to the saveAs method, done. No need to close the file etc… Works perfectly in my plugin. Just add some try-catch blocks around the different parts in your code to debug, make sure you have the right permissions in the manifest and make sure that the path you give to getEntryWithUrl is correctly formed.

I would recommend debugging this step by step. 1) First I would make sure that you can obtain an entry to your output folder. Check that you obtained the folder by asking it for the native path and check that it is a folder (isFolder()). 2) Make sure that you can create a new file in that folder (again that’s just a handle).

If you don’t get anywhere with this, please share your code (specifically how you obtain your output folder onwards).

One last thing: saveAs returns a promise. I’m not sure that the docs are up to date about this, but you have to put an ‘await’ in front. I corrected my pseudocode accordingly. :slight_smile:

yes, the API didn’t mention that saveAs is asynchronous. That is where the problem was. thanks!

Yay! Glad it worked out. :partying_face:

Ah, yes. The documentation generation for saveAs is still a bit of problem since it is technically a property. I do have that on my list of items to resolve.

In the mean time, I will update the sample code to properly await the calls.