Export JPEG and get file size in UXP

Hello, the link below might help for saving a file in UXP.
Anything to do with files read/write is needed to be approached differently than in CEP

https://forums.creativeclouddeveloper.com/t/saving-a-jpg-to-a-selected-folder/2838

This link provide essential info about files in UXP:
https://www.adobe.io/photoshop/uxp/uxp/reference-js/Modules/uxp/Persistent%20File%20Storage/File/

Thanks for the quick response. As a workaround I think I’ll have to save as jpeg, then reopen, resize and save for now until this basic functionality will be available in UXP API.

I’m confused now, what exactly are you after?

Actually I am able to save the document with the new workflow (entry, tokens). I just need to export PSD to jpeg setting the width and height. But unfortunately Export feature is not available at all. Do you have suggestion on this?

Typically what I do is that I duplicate the current document PSD, I resize it, sharpen it and what not, and then export that PSD to jpeg file.

This will trigger the save process but saving will be done in the background.
Reading metadata right even after doing await on the descriptor will return “no such file or directory” error.

Unfortunately I didn’t yet find a reliable way to save/export to JPG and get file size.
There also seem to be many strange issues with files overall in UXP: How to overwrite a file?

I agree that the best way would be to have a Promise resolve once the file saving is done.
If that’s not available however (again, I haven’t tested saving JPGs myself), I guess you’ll have to implement some kind of polling mechanism:

  1. Trigger file saving
  2. Try to read the entry in a loop (every X ms)
  3. Once it’s there, read the metadata & filesize

File saving can take a significant amount of time of course, which you have to consider. While JPGs save relatively fast, I already had some large PNGs take 10-30 seconds to write to the disc.

1 Like

I will try something along those lines.
For temporary files waiting 5 seconds didn’t help, file still doesn’t exist

I’m trying to export/save multiple JPEGs using my plugin, the images are for products with white background so quality is not that important.

however, I’m losing my sanity and I’d appreciate any help I can get.

right now I’m using doc.save(myFile, jpegOptions); and the size is way too big for what I’m saving and quality doesn’t have any noticeable effect on size.

while export reduces the size from 1.5MB to 66KB with acceptable quality for my use.
is there ANY way I can use export in my code?

I’m willing to use ExtendScript/JSX/UXP or whatever

While technically doc.save is not the same thing as an export, I’m surprised that modifying the quality or other parameters isn’t getting the file size down to something reasonable.

Can you share the code snippets you’ve used for doc.save along w/ some data on how much (if at all) those shrank the file?

Oh, to answer your specific question:

I’d be surprised if there isn’t a batchPlay command that would invoke the Export process. If you haven’t already, check out @Jarda 's excellent Alchemist plugin to record the events that are getting sent around to get a feel for what that might look like.

(Note: I’m no expert on what is batchplayable and what isn’t – but this feels like something that would be. Maybe @heewoo has an idea here.)

here’s my stripped-down code

let jpegOptions = new Object;
// soQuality is a <sp-slider> with min="0" max="12"
var soQuality = document.getElementById("soQuality")
jpegOptions.quality =  soQuality ? soQuality.value : 10;
jpegOptions.embedColorProfile = true;
jpegOptions.matte = 1;
// requestEntry is a custom function
// I use it to manage (get) and store entries
myEntry = await requestEntry(docPath);
thedoc.save(myEntry, jpegOptions);

I got a file size range from 1,561 KB (jpegOptions.quality = 0)
and up to 1,747 KB (jpegOptions.quality = 12)
with sizes in between for the rest (1-11)

using “Export As” I got a file size of 111 KB with a quality value of 6 (7 being the max)

as others pointed out, Alchemist is unable to listen to Export for some reason. it only catches the invokeCommand Event which is of no use to me since I want the whole process automated.

thank you so much for looking into this!

I think that might mean you are using the version of Alchemist from the marketplace rather than Jarda’s development version:

The marketplace version doesn’t capture all PS events.

that’s what I thought at first (reading the warning message) so I already tried using the UXP Dev Tool to load the Plugin and got the exact same result:
invokeCommand

{
   "_obj": "invokeCommand",
   "commandID": 3443,
   "kcanDispatchWhileModal": true,
   "_isCommand": false
}

featureInfo

{
   "_obj": "featureInfo",
   "active": false,
   "command": "getFeatureActive",
   "dontRecord": true,
   "forceNotify": true,
   "_isCommand": false
}

Is there any further information on this? Is there any way to export a file using UXP? I can confirm that the Alchemist plugin, even the development version I installed via the UXP Developer Tool, returns zero events for any file export.

This seems like it should be utterly basic functionality.

2 Likes

I ended up using legacy export, which can be recorded by actions/Alchemist

I remember seeing export in the road map under (far future) more than a year ago I think

I also read somewhere in the forums that export functionality is available to be used under the hood, I just didn’t manage to figure out the parameters since it’s private and undocumented

TLDR: not available yet
you can record legacy export
you can use quick export which only allows choosing the target folder but not the filename

Can the “export” you are talking about be substituted with “saveAs”?

Here’s an example

Document about saveAs

Document about save options

It cannot, because I need to be able to downscale on exporting. I do wish that I could use it, though!

@Maher Thanks so much for this suggestion! This looks very promising, though, I must admit, this is one of the more confusing event descriptors I’ve come across. Would you happen to have any further information on what the legacy export parameters mean?

property names are a mess, for my case I used known value in the properties I want to be dynamic and looked for that value and replaced it with my own variables.

async function quickExport(folder, filename, quality) {
   await executeAsModal(async () => {
      let command = { "_obj": "export", "using": { "$DIDr": true, "$EICC": false, "$Mtt": false, "$MttB": 255, "$MttG": 255, "$MttR": 255, "$Op": { "_enum": "$SWOp", "_value": "$OpSa" }, "$Pass": 1, "$QCUI": 0, "$QChS": 0, "$QChT": false, "$QChV": false, "$SHTM": false, "$SImg": true, "$SWch": { "_enum": "$STch", "_value": "$CHsR" }, "$SWmd": { "_enum": "$STmd", "_value": "$MDCC" }, "$SWsl": { "_enum": "$STsl", "_value": "$SLAl" }, "$obCS": { "_enum": "$STcs", "_value": "$CS01" }, "$obIA": false, "$obIP": "", "$ohAA": true, "$ohAC": { "_enum": "$SToc", "_value": "$OC03" }, "$ohCA": false, "$ohEn": { "_enum": "$STen", "_value": "$EN00" }, "$ohIC": true, "$ohIZ": true, "$ohIn": -1, "$ohLE": { "_enum": "$STle", "_value": "$LE03" }, "$ohQA": true, "$ohTC": { "_enum": "$SToc", "_value": "$OC03" }, "$ohXH": false, "$olCS": false, "$olEC": { "_enum": "$STst", "_value": "$ST00" }, "$olNC": [{ "$ncTp": { "_enum": "$STnc", "_value": "$NC00" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC19" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC28" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }], "$olSH": { "_enum": "$STsp", "_value": "$SP04" }, "$olSV": { "_enum": "$STsp", "_value": "$SP04" }, "$olWH": { "_enum": "$STwh", "_value": "$WH01" }, "$ovCB": true, "$ovCM": false, "$ovCU": true, "$ovCW": true, "$ovFN": filename, "$ovNC": [{ "$ncTp": { "_enum": "$STnc", "_value": "$NC01" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC20" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC02" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC19" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC06" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC24" }, "_obj": "$SCnc" }, { "$ncTp": { "_enum": "$STnc", "_value": "$NC22" }, "_obj": "$SCnc" }], "$ovSF": true, "$ovSN": "images", "_obj": "SaveForWeb", "blur": 0.0, "format": { "_enum": "$IRFm", "_value": "JPEG" }, "in": { "_kind": "local", "_path": folder }, "interfaceIconFrameDimmed": false, "optimized": true, "quality": quality } };
      result = await batchPlay([command], {});
   }, { "commandName": "Exporting..." });
}

the example above exports JPG using a token, filename and quality