Roswell Studios

139 Fulton Street, Ste 132
New York, NY 10038

Converting Product Images on Shopify

— January 8, 2018

First, it is easier and generally preferable to do this in the product uploading import CSV.

But lets say the upload has already occurred, against your advice they used giant PNG images and now it is your problem to fix it because unsurprisingly the collection page loads slowly.

This is not something the admin console can do, so here’s a PHP script to download png images, convert to jpg, compress, then re-upload all the product images on a Shopify store.

This is very slow. Re-using curl, possibly the jpegtran process could speed it up, but so does just starting 4 of these at a time with different page numbers.

[NOTE: this is not a turnkey solution, and assumes you have a Mac environment, or Linux with php,gd, and where ever jpegtran came from. You will need to be able to edit and run a PHP script.]

store . $url);

curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $httpAction);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
if (!empty($data)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json"
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
$output = curl_exec($ch);
if ($output === false) {
throw new Exception(curl_error($ch));
} else {
//return the output as an object
$outObj = json_decode($output);
return $outObj;

public function run() {
//get products
$page = 1;
while (true) {
echo "PAGE: $page\n";
$prodlist = $this->sendShopifyCmd('/admin/products.json?fields=id,images&page=' . $page);
if (!$prodlist->products) {

foreach ($prodlist->products as $prod) {
foreach ($prod->images as $img) {
//get image

preg_match('/(PROD_.*\.)png/', $img->src, $matches);
if (!isset($matches[1])) {
continue; //could have been converted already
$filename = $matches[1] . 'jpg';

// Get start image with transparency
$src = imagecreatefromstring(file_get_contents($img->src));
// Get width and height
$w = imagesx($src);
$h = imagesy($src);

// Make a background canvas, same size, to overlay onto
$result = imagecreatetruecolor($w, $h);
$blue = imagecolorallocate($result, 252, 252, 252); #fcfcfc
imagefill($result, 0, 0, $blue);

// Overlay start image ontop of canvas
imagecopyresampled($result, $src, 0, 0, 0, 0, $w, $h, $w, $h);

// Save result
$jpg = ob_get_contents();


$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "r")
$process = proc_open('jpegtran -optimize -copy none ', $descriptorspec, $pipes, null, null);
if (is_resource($process)) {
fwrite($pipes[0], $jpg);
$jpg = stream_get_contents($pipes[1]);

$return_value = proc_close($process);

//post new image
$ret = $this->sendShopifyCmd('/admin/products/' . $prod->id . '/images/' . $img->id . '.json', 'DELETE');
$ret = $this->sendShopifyCmd('/admin/products/' . $prod->id . '/images.json', 'POST', ['image' => [
'attachment' => base64_encode($jpg),
'filename' => $filename,
'position' => $img->position,
'variant_ids' => $img->variant_ids
//cheaper, but doesn't seem to work? The filename change in particular might be bad
//$ret = $this->sendShopifyCmd('/admin/products/'.$prod->id.'/images/'. $img->id .'.json', 'PUT', ['image'=>['id'=>$img->id,'filename'=>$filename,'attachment'=> base64_encode($jpg)]]);
//file_put_contents('tmp.jpg', $jpg);
//exit();//1 image
//exit();//1 product
//exit();//1 page


$ptoj = new ptoj();

Back to all