如何从Google Apps脚本访问新的“单元格内图片”?

时间:2019-06-24 09:13:16

标签: google-apps-script google-sheets gas

新功能“插入”>“图像”>“ Google表格中的单元格中的图像”将图像插入单元格中,而不是作为OverGridImage插入。 我想以这种方式插入图片,然后从Google Apps脚本访问图片。这可能吗?

插入图像后,选择单元格时该单元格的公式为空白。我尝试搜索GAS参考,但找不到有关此相对较新功能的任何信息。 有关过度网格图像的信息。我希望单元内图像具有相似的功能。

我已经尝试过这样的事情:

// See what information is available on a cell with inserted image:
var image = sheet.getRange(1, 1).getFormula();
Logger.log(image);

日志显示为空。 我尝试了几种:.getImage()(不存在)、. getValue()、. getFormula()

我希望能够以某种方式访问​​图像URL或Blob。

6 个答案:

答案 0 :(得分:2)

我相信您的目标如下。

  • 您要使用Google Apps脚本在Google Spreadsheet的单元格中检索图像。

问题和解决方法:

不幸的是,在当前阶段,尚没有方法可以在Spreadsheet服务和Sheets API中检索Spreadsheet上单元格中的图像。 Rafa Guillermo's answer已经提到了这一点。因此,在这个答案中,我想提出一种解决方法,以使用Google Apps脚本检索单元格中的图像。

在这种解决方法中,我使用从Google Spreadsheet转换的Microsoft Excel数据。即使将Google Spreadsheet转换为Microsoft Excel Data,也不会删除单元格中的图像。我用这个当然,还可以从Spreadsheet转换的HTML数据中检索图像。但是在这种情况下,HTML数据的解析比Excel数据的解析有点复杂。因此,在这里,我想建议从从Spreadsheet转换的Excel数据中检索图像。此解决方法的流程如下。

  1. 使用云端硬盘API将Google Spreadsheet转换为Microsoft Excel(XLSX数据)。
  2. 使用Google Apps脚本解析XLSX数据。
    • 解压缩转换后的XLSX数据后,可以将其分析为XML数据。幸运的是,在Microsoft Docs,详细规范以Open XML的形式发布。因此,在这种情况下,可以使用Google Apps脚本的XmlService分析Microsoft文档(例如XLSX,DOCX和PPTX)。我认为这种方法在其他情况下也很有用。
  3. 从XLSX数据检索图像。

模式1:

在这种模式下,我想介绍一种简单的方法。

示例脚本:

function myFunction() {
  const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  const url = "https://docs.google.com/spreadsheets/export?exportFormat=xlsx&id=" + spreadsheetId;
  const blob = UrlFetchApp.fetch(url, {headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`}}).getBlob().setContentType(MimeType.ZIP);
  const xlsx = Utilities.unzip(blob);
  xlsx.forEach(b => {
    const name = b.getName().match(/xl\/media\/(.+)/);
    if (name) DriveApp.createFile(b.setName(name[1]));
  });
}
  • 在此示例脚本中,电子表格中的所有图像均作为文件导出。因此,在这种情况下,将检索电子表格中所有工作表的单元格中以及单元格上方的图像。而且,它无法检索图像在单元格中的单元格坐标。
    • 在当前阶段,没有任何方法可以检索Google Spreadsheet中的图像作为Blob。在此示例脚本中,可以实现这一点。
  • 此示例脚本无法导出工程图。请注意这一点。
  • 不使用setContentType(MimeType.ZIP)时,在Utilities.unzip(blob)处发生错误。请注意这一点。

模式2:

在此模式下,使用表格名称和单元格坐标从Spreadsheet检索图像。在这种情况下,脚本会变得有些复杂。因此,在这里,我想介绍一个使用Google Apps脚本库的示例脚本。 Ref当然,您可以在那里看到整个脚本。

示例脚本:

在使用此脚本之前,请安装Google Apps脚本库的DocsServiceApp(此GAS库的作者是tanaike。)。 Ref并运行myFunction的功能。

function myFunction() {
  const cell = "A1";
  const sheetName = "Sheet1";
  const spreadsheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
  const obj = DocsServiceApp.openBySpreadsheetId(spreadsheetId).getSheetByName(sheetName).getImages();
  console.log(obj)
  const blobs = obj.filter(({range, image}) => range.a1Notation == cell && image.innerCell);
  console.log(blobs.length)
  if (blobs.length > 0) DriveApp.createFile(blobs[0].image.blob);
}
  • 在此示例中,将检索活动电子表格中“ Sheet1”的单元格“ A1”中的图像,并将检索到的blob作为图像文件创建到根文件夹中。

注意:

  • 在当前阶段,当将图像插入Google Spreadsheet并将Spreadsheet转换为XLSX数据时,包含XLSX数据的图像的文件名分别为image1image2,不是原始文件名。因此,这似乎是当前的规范。
  • 从XLSX数据中检索图像时,该图像似乎与原始图像有些不同。图像格式是相同的。但是数据大小小于原始数据大小。当图像尺寸大于2048像素和72 dpi时,图像被修改为2048像素和72 dpi。即使图像尺寸小于2048像素和72 dpi,文件尺寸也会小于原始尺寸。因此,我认为图像可能会被压缩。请注意这一点。
  • 在当前阶段,无法直接检索图形。

参考文献:

答案 1 :(得分:2)

Rafa Guillermo和Tanaike都要求我根据对Tanaike帖子的评论做出回答。我在下面这样做,但是它属于解决方法的类别,而不是“答案”的类别。一个正确的答案将解决原始帖子中的确切问题。

正如我在评论中所说,我已经在简单的情况下使用了此方法,并且还进行了一些测试,表明该方法可以保留图像分辨率。由于我仅在以下简单情况下使用过此方法,因此我不知道它的一般工作原理。

下面提供的步骤是(尽我所能)我记得做一个特定示例时所经历的步骤。使用此方法后,这是最终结果的前十二行:

enter image description here

此示例总共有7100多行

  • 第1列包含430多个图像或空白单元格,其中大多数重复 多次

    第2列包含每个图片的唯一ID

    第3列是使用 下面的方法

从Google表格单元格提取图像的步骤:

  1. 将包含图像的列和行调整为较大的大小(例如300)
  2. 使用文件>发布到网络并将生成的链接粘贴到新标签页中
  3. 在Chrome中,使用“文件”>“页面另存为...”>“完成的网页”
  4. 将在以_files结尾的html文件夹中找到图像
  5. 如果需要,请重命名文件以使用图像扩展名并按顺序列出*

要将下载的图像文件名键入到工作表中的图像单元格中,请执行以下操作:

  1. 重复工作表,因为以下内容将删除原始数据
  2. 选择包含图像和ID的列,然后使用“数据”>“删除重复项”
  3. 在包含文件名的ID旁边添加新列**
  4. 使用VLOOKUP函数根据唯一ID将所有文件名传输到原始工作表中***

*在我的示例中,所有图像均具有p.txt,p(1).txt,p(2).txt等名称。在Mac OS Finder中,我选择了所有文件,并右键单击了“重命名文件”…然后使用replace选项将.txt替换为.jpg,将(1)替换为(001),依此类推…

**例如,可以使用Terminal ls -l命令获取文件名列表

***例如,我使用了=vlookup(B2,unique!$B$2:$C$430,2,false)

答案 2 :(得分:1)

我只是尝试了一些非常基本的东西,它奏效了。也许并非在所有情况下都有效,取决于您之前是否通过公式添加了图像...

  • 通过 Google Apps 脚本添加图片:
var ss = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
  var sheet = ss.getSheetByName(SHEET_NAME);

  sheet.getRange('A1').setFormula('=IMAGE("https://developers.google.com/google-ads/scripts/images/reports.png")');

工作(它在单元格中,将自动适应调整大小):

enter image description here

  • 然后从单元格中检索图像网址:
var imgVal = sheet.getRange('A1').getFormula();

var regEx = /"(.*)"/gm;
var url = regEx.exec(imgVal)[1];

Logger.log(url);

日志将是:

enter image description here

答案 3 :(得分:1)

这个问题有点老了,但既然我今天遇到了这个问题,请允许我分享我的经验。

我意识到单元格的 getValue() 返回一个对象,它的文本是“CellImage”。这让我明白在这个单元格中有一个嵌入的图像。此对象似乎与 OverGridImage 对象相似(或相同)。至少,您可以使用 getAltTextTitlegetAltTextDescription 方法。

通过结合所有这些功能,我的解决方法是:

  • 向单元格中的图像添加特定的 AltText。
  • 获取对象中单元格的值。
  • 检查这是否等于“CellImage”。
  • 如果是 CellImage,获取 AltText。
  • 根据这个 AltText 的值做任何你喜欢的事情。

示例代码如下:

/*-------------------------------------------------------------------------
 Custom event handler triggered when a single cell is selected in the spreadsheet.
 @param {Event} e The onSelectionChange event.
-------------------------------------------------------------------------*/
function onSingleCellSelected(e) {
  var cell = e.range.getCell(1, 1);
  var v = cell.getValue();
  if(v == "CellImage") {
    var altText = v.getAltTextTitle();
    Logger.log(v.getAltTextDescription());
    if(altText == "@action(recordTime)"){
      cell.setBackground("cyan");
    }
  }
}

答案 4 :(得分:0)

我来自G-Suite支持。这是一项新功能,遗憾的是,目前还没有一种方法可以使用Google Apps脚本或Sheets API将图像插入到单元格中。尝试使用具有以下参数的spreadsheets.get方法获取单元格中的数据

  spreadsheetId: "ID of private spreadsheet created in Drive"
  includeGridData: True
  ranges: D7
  fields: sheets/data/rowData/values

将返回200 response,但是不返回图像数据:

{
  "sheets": [
    {
      "data": [
        {
          "rowData": [
            {
              "values": [
                {
                  "userEnteredValue": {},
                  "effectiveValue": {},
                  "effectiveFormat": {
                    "backgroundColor": {
                      "red": 1,
                      "green": 1,
                      "blue": 1
                    },
                    "padding": {
                      "top": 2,
                      "right": 3,
                      "bottom": 2,
                      "left": 3
                    },
                    "horizontalAlignment": "LEFT",
                    "verticalAlignment": "BOTTOM",
                    "wrapStrategy": "OVERFLOW_CELL",
                    "textFormat": {
                      "foregroundColor": {},
                      "fontFamily": "Arial",
                      "fontSize": 10,
                      "bold": false,
                      "italic": false,
                      "strikethrough": false,
                      "underline": false
                    },
                    "hyperlinkDisplayType": "PLAIN_TEXT"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

但是,在Google的问题跟踪器上有一项针对此功能的请求,您可以找到here。如果您转到功能请求页面并单击左上角的星号,则可以让Google知道您也希望使用此功能,并会自动获取有关其进度的更新。

答案 5 :(得分:0)

这个答案是关于插入细胞内图像。我还没有找到实际提取图像数据的方法,因此 Panos's answer 是读取单元格内图像数据的最佳选择。

有几种不同的方法可以做到这一点,其中一些使用一些未记录的 API。

1. =IMAGE(<http url>)

=IMAGE 是一个标准函数,它显示在单元格内的图像中。它几乎与手动插入内嵌图像完全相同。

2.按值复制 =IMAGE

一旦你有一个 =IMAGE 图像,你可以复制它并按值粘贴它,这将复制图像没有公式(如果你出于某种原因想要那个)。您可以使用 copyTo function:

在脚本中执行此操作
srcImageRange.copyTo(dstRange, { contentsOnly: true })

这个无公式的图像只能与真正的单元格内图像区分开来,因为当您右键单击它时,它会缺少“替代文本”和“将图像置于单元格上”上下文菜单选项。这些选项仅显示在真实的细胞内图像上。

3.未记录的 CellImage API

当您对单元格内图像(公式和手动插入)调用 getValue() 时,您会得到一个 CellImage 实例。

细胞图像

<头>
道具/方法 (返回)类型 说明
toString() string 返回"CellImage"
getContentUrl() ? 总是抛出错误?
toBuilder() CellImageBuilder 将其转换为可写的 CellImageBuilder 实例。
getAltTextDescription() string 返回替代文本描述。
getAltTextTitle() string 返回替代文本标题。
getUrl() ? 似乎不起作用,总是返回未定义。 :(
valueType ? SpreadsheetApp.ValueType一样,好像没什么意义。

CellImageBuilder

具有与 CellImage 相同的所有属性和方法以及这些附加属性:

<头>
道具/方法 (返回)类型 说明
toString() string 返回"CellImageBuilder"
build() CellImage 转换为(只读)CellImage 实例。
setSourceUrl(string) void 通过提供网络或数据 URL 更新图像。
setAltTextTitle(string) void 设置替代文本标题。
setAltTextDescription(string) void 设置替代文字描述。

我看到在 IMAGE() 上使用它的主要好处是它支持数据 URL,因此间接支持 blob。

工作示例代码

请记住,未记录的 API 可能会更改,恕不另行通知。

Link to Example Spreadhseet

// 1 (or just use IMAGE in formula directly)
function insertImageFormula(range, httpUrl) {
  range.setFormula(`=IMAGE("${httpUrl}")`);
}

// 2
function insertImageValue(range, httpUrl) {
  range.setFormula(`=IMAGE("${httpUrl}")`);
  SpreadsheetApp.flush(); // Flush needed for image to load.
  range.copyTo(range, { contentsOnly: true }); // Copy value onto itself, removing the formula.
}

// 3
function insertCellImage(range, sourceUrl) {
  range.setFormula('=IMAGE("http")'); // Set blank image to get CellImageBuilder handle.
  const builder = range.getValue().toBuilder();
  builder.setSourceUrl(sourceUrl);
  builder.setAltTextDescription(sourceUrl); // Put url in description for later identification, for example.
  range.setValue(builder.build());
}

const DATA_URI = "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7///"
  + "/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAos"
  + "J6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7";

function test() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  sheet.clear();

  sheet.getRange(1, 1).setValue("IMAGE formula");
  insertImageFormula(sheet.getRange(2, 1), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");
  
  sheet.getRange(1, 2).setValue("Copied-by-value IMAGE");
  insertImageValue(sheet.getRange(2, 2), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");

  sheet.getRange(1, 3).setValue("In-Cell Image (Http URL)");
  insertCellImage(sheet.getRange(2, 3), "https://www.google.com/images/icons/illustrations/paper_pencil-y128.png");

  sheet.getRange(1, 4).setValue("In-Cell Image (DATA URI)");
  insertCellImage(sheet.getRange(2, 4), DATA_URI);

  sheet.getRange(1, 5).setValue("In-Cell Image (Blob DATA URI)");
  const blob = UrlFetchApp.fetch("https://www.gstatic.com/script/apps_script_1x_24dp.png").getBlob();
  insertCellImage(sheet.getRange(2, 5), blobToDataUrl(blob));
}

function blobToDataUrl(blob) {
  return `data:${blob.getContentType()};base64,${Utilities.base64Encode(blob.getBytes())}`
}