18

In google apps Script you can insert an image into Google Spreadsheets using the insertImage function (https://developers.google.com/apps-script/reference/spreadsheet/sheet#insertimageblob-column-row).

But I'm not using appscript. I'm using the Google Sheets API (https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets) and I can't seem to find a way to do this. Is there any possible implementation?

dedles
  • 606
  • 2
  • 8
  • 20

3 Answers3

20

The V4 API doesn't have the ability to insert an image blob like Apps Script does (where the image is an overlay on the sheet, not associated with any cell). You may be able to workaround this using the =IMAGE function. We know of the gap in functionality and are looking into adding support for image blobs.

Sam Berlin
  • 3,603
  • 12
  • 23
8

Set the formula with script like this:

function showImage() {
 var ss=SpreadsheetApp.getActiveSpreadsheet() 
 var formulaSheet = ss.getSheetByName("Sheet1");
 var formulaCell = formulaSheet.getRange("B5");
 formulaCell.setFormula('=IMAGE("http://finviz.com/fut_chart.ashx?t=ES&p&p=m5&s=m",4,100,200)')
}
Ed Nelson
  • 10,015
  • 2
  • 27
  • 29
  • He Nelson, Thank you so much for the snippet of code. It's so valueable... if you have a website where you share google sheets contents. Let me know... This is great!!! – xzibit Aug 12 '20 at 00:45
  • This image formula will not be exported to XLS format. It's better to use Google Apps Script Web App gateway. – Viacheslav Dobromyslov Aug 20 '20 at 05:30
  • 1
    Unfortunately this only supports images hosted in the web which is usually not what you want when using the Google Sheets API. It does not support BLOB and also no data URLs. – ndreisg May 23 '22 at 16:19
6

In the interim, this Google Apps Script Web App will do it (I recommend hosting your own rather than using this one, for privacy and security).

This works with either JSON or application/x-www-form-urlencoded, and whether the URL passed is a link or an actual base64 encoded image url like you might get from https://www.base64-image.de/.

function doGet(e) {
  return ContentService.createTextOutput("Authorization: Bearer " + ScriptApp.getOAuthToken())
}

//
// Example curl command to insert an image:
// 
// curl -L -d '{ "spreadsheetid": "1xNDWJXOekpBBV2hPseQwCRR8Qs4LcLOcSLDadVqDA0E","sheetname": "Sheet1", "imageurl": "https://www.google.com/images/srpr/logo3w.png", "column": 1, "row": 1 }' \
// -H "Authorization: Bearer <INSERT TOKEN RETURNED FROM GET HERE>" \
// -H 'Content-Type: application/json' \
// https://script.google.com/a/tillerhq.com/macros/s/AKfycbzjFgIrgCfZTvOHImuX54G90VuAgmyfz2cmaKjrsNFrTzcLpNk0/exec
//

var REQUIRED_PARAMS = [
  'spreadsheetid', // example: "1xNDWJXOekpBBV2hPseQwCRR8Qs4LcLOcSLDadVqDA0E"
  'sheetname',     // Case-sensitive; example: "Sheet1"
  'imageurl',      // Can be an url such as "https://www.google.com/images/srpr/logo3w.png"
                   // or alternately "data:image/png;base64,iVBOR...<snip>...gg=="
  'column', // 1-based (i.e. top left corner is column 1)
  'row'     // 1-based (i.e. top left corner is row 1)
];

function doPost(e) {

  var result = {
    status: "ok",
    defaultMessage: "Image inserted."
  }

  try {
    var params = (e.postData && e.postData.type == "application/x-www-form-urlencoded") ? e.parameter
    : (e.postData && e.postData.type == "application/json") ? JSON.parse(e.postData.contents)
    : undefined;


    if (!params) throw new Error('Unsupported content-type, must be either application/x-www-form-urlencoded or application/json.');

    REQUIRED_PARAMS.forEach(function(requiredParam) {
      if (!params[requiredParam]) throw new Error('Missing required parameter ' + requiredParam);
    });

    SpreadsheetApp.openById(params.spreadsheetid).getSheetByName(params.sheetname).insertImage(params.imageurl, params.column, params.row);  

  } catch(e) {

    console.error(e); 

    result.status = "error";
    result.error = e;
    result.defaultMessage = e.message;

  }  

  return ContentService.createTextOutput(JSON.stringify(result))
    .setMimeType(ContentService.MimeType.JSON)  
}

Two puzzling things I never figured out:

It worked great all along from within Postman (presumably authed with a cookie) after visiting the URL for the web app and accepting permissions. Unfortunately, I wasn't able to get it working from curl with the Oauth token returned in ScriptApp.getOAuthToken() until I manually added https://www.googleapis.com/auth/drive in the manifest -- which is still a bit of a head scratcher for me.

Here's my resulting manifest:

{
  "timeZone": "America/Los_Angeles",
  "dependencies": {
  },
  "webapp": {
    "access": "ANYONE",
    "executeAs": "USER_ACCESSING"
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes": ["https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive"]
}

I was also never able to get it to work converting to a Blob and passing that in to insertImage(), but the URL flavor of insertImage works great with full Base 64 encoded image URLs, so that's a bit annoying but this seems reasonably workable until the Sheets API gets the functionality.

The script (source) itself is shared read-only with the world here:

https://script.google.com/d/1JvFwemL45x3orxFiJf_Gye-JWXaFlzA_MysJsQx06LsH8M2psa9i1H99/edit?usp=sharing

And it's also publicly deployed here, wo if you want to test it out without deploying your own, have at it:

https://script.google.com/a/tillerhq.com/macros/s/AKfycbzjFgIrgCfZTvOHImuX54G90VuAgmyfz2cmaKjrsNFrTzcLpNk0/exec

  • Tim
Timothy Johns
  • 1,075
  • 7
  • 17
  • 1
    One more note here -- if you do deploy your own rather than just using this one, it is absolutely critically imperative to keep the executeAs "USER_ACCESSING" -- otherwise that doGet route is a handy-dandy provider of YOUR access tokens, with access to most of the Drive and Sheets APIs. – Timothy Johns Feb 22 '18 at 18:04
  • How to display datepicker by double click on specific column using rest API? What is the custom validation rule for this? – Parthiv Jul 05 '18 at 11:18
  • When we send a blob for the imageURL we are getting the following error - result : 200 content : {"status":"error","defaultMessage":"Error retrieving image from URL or bad URL: data:image/png;base64, – Jack tileman Oct 01 '19 at 23:00
  • dropoff_signature = "ZGF0YT WVhSaA==" web_app_url = "script.google.com/macros/s/A < clip > y/exec" image_data = "data:image/png;base64," + dropoff_signature data_to_post = { 'spreadsheetid' : spreadsheet_Id, 'sheetname' : 'Sheet1', 'imageurl' : image_data, 'column' : 5, 'row' : 5 } encoded_data = urllib.urlencode(data_to_post) # Send encoded data to application-2 url_result = urlfetch.fetch(web_app_url, encoded_data, method='POST') This is how we are calling our webapp scripts – Jack tileman Oct 01 '19 at 23:01
  • Can the do post method be called by a service account? how to call it? what are the necessary credentials/permissions etc? – Peter K Jun 03 '21 at 06:15