简体   繁体   English

如何在GAS中通过SOAP API发送带有附件的DIME消息?

[英]How do I send a DIME message with an attachment via SOAP API in GAS?

I have an instance of SDL's WorldServer that I am trying to access via SOAP to create new projects. 我有一个SDL WorldServer实例,试图通过SOAP访问它以创建新项目。

I was thinking to make a web app via Google Apps Script where users could upload files into blobs that I could use to send and make projects / quotes. 我当时正在考虑通过Google Apps脚本制作一个网络应用,用户可以在其中将文件上传到Blob中,以用于发送和制作项目/报价。 I was able to get SOAP working fine for any commands that don't require attachments. 对于不需要附件的任何命令,我都能使SOAP正常工作。 But adding attachments complicated things. 但是添加附件会使事情复杂化。

I have succeeded in doing this through soapui (However, soapui takes care of the DIME formatting for me, so I am assuming I need to use DIME?), but when I attempt to send the same SOAP message by manually generating the message myself, I get a SOAP fault stating: 我已经通过soapui成功做到了这一点(但是,soapui为我处理了DIME格式,所以我假设我需要使用DIME吗?),但是当我尝试通过自己手动生成消息来发送相同的SOAP消息时,我收到一个SOAP错误说明:

<faultcode>soapenv:Server.userException</faultcode>
<faultstring>java.io.IOException: Stream closed.</faultstring>
<detail><ns1:hostname xmlns:ns1="http://xml.apache.org/axis/">SNJWS112</ns1:hostname></detail>

Here is the sample code I am using: 这是我正在使用的示例代码:

function makeDimeHeader (position, isChunk, id, contentType, data) {
  // ref: http://xml.coverpages.org/draft-nielsen-dime-02.txt
  var bin = ""

  // version 1
  bin += "00001"

  if (position == "start") {
    bin += "10"
  } else if (position == "finish") {
    bin += "01"
  } else if (position == "middle") {
    bin += "00"
  } else if (position == "only") {
    bin += "11"
  } else {
    throw "Error: position must be 'start', 'only', 'middle', or 'finish'"
  }

  if (isChunk) {
    bin += "1"
  } else {
    bin += "0"
  }

  if (id.constructor !== Array) throw "Error: id must be a byte array"
  if (contentType.constructor !== Array) throw "Error: contentType must be a byte array"
  if (data.constructor !== Array) throw "Error: data must be a byte array"

  var textType = Utilities.newBlob(contentType).getDataAsString()

  if (textType.slice(0,4) == "http") {
    bin += "0010"
  } else if (contentType.length == 0) {
    bin += "0000"
  } else {
    bin += "0001"
  }

  // RESERVED
  bin += "0000"

  // OPTIONS LENGTH
  bin += "0000000000000000"

  bin += ("0000000000000000" + id.length.toString(2)).slice(-16)

  bin += ("0000000000000000" + contentType.length.toString(2)).slice(-16)

  bin += ("00000000000000000000000000000000" + data.length.toString(2)).slice(-32)

  var bytes = []
  for (var i = 0; i < bin.length/8; i++) {
    num = parseInt(bin.slice(i*8,(i*8)+8),2);
    // Byte[] is an Int8Array, not Uint8Array
    if (num > 127) num = num - 256
    bytes.push(num)
  }

  // PADDING TO CLOSEST 4 BYTES
  if (id.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (id.length % 4)))
  bytes = bytes.concat(id)

  // PADDING TO CLOSEST 4 BYTES
  if (contentType.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (contentType.length % 4)))
  bytes = bytes.concat(contentType)

  // PADDING TO CLOSEST 4 BYTES
  if (data.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (data.length % 4)))
  bytes = bytes.concat(data)

  return bytes

}

function testPost() {
  var file = UrlFetchApp.fetch("https://.../somedoc.xlsx");
  var fileBlob = file.getBlob();
  var fileBytes = fileBlob.getBytes();

  var contentType = 'application/dime; charset=Utf-8';

  var data = Utilities.newBlob(
    "<?xml version=\"1.0\"?>\r\n" +
    "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:com=\"http://www.idiominc.org/com.idiominc.webservices.WorkflowWSWorkflowManager\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
    "<soapenv:Header/>" +
    "<soapenv:Body>" +
    "<com:createProjectGroup7_ soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
    "<token xsi:type=\"xsd:string\">0000000000</token>" +
    "<name xsi:type=\"xsd:string\">TEST NEW PROJECT</name>" +
    "<locales xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\">" +
    "<item type=\"xsd:string\">English (United States)</item>" +
    "<item type=\"xsd:string\">Korean</item>" +
    "</locales>" +
    "<attachedFile xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\">" +
    "<file type=\"xsd:string\">test.xlsx</file>" +
    "</attachedFile>" +
    "<client xsi:type=\"xsd:string\">TEST CLIENT</client>" +
    "<projectType xsi:type=\"xsd:string\">TEST PROJECT TYPE</projectType>" +
    "<customAisProperties xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\"/>" +
    "</com:createProjectGroup7_>" +
    "</soapenv:Body>" +
    "</soapenv:Envelope>").getBytes()

  var soapcontent = Utilities.newBlob(
    "http://schemas.xmlsoap.org/soap/envelope/"
    ).getBytes()

  var firstSoapDime = makeDimeHeader("start", false, [], soapcontent, data)

  var filecontent = Utilities.newBlob(
    "application/octet-stream"
    ).getBytes()

  var lastFileData = makeDimeHeader("finish", false, Utilities.newBlob("test.xlsx").getBytes(), filecontent, fileBytes)

  var payloadbytes = firstSoapDime.concat(lastFileData)

  var headers =
      {
        "SOAPAction" : ""
      };

  var payload = Utilities.newBlob(payloadbytes).getDataAsString();

  var options =
      {
        "method" : "post",
        "headers" : headers,
        "contentType": contentType,
        "contentLength": payloadbytes.length,
        "payload" : payload,
        "muteHttpExceptions" : true
      };

  var response = UrlFetchApp.fetch("https://<ourdomain>.sdlproducts.com/ws/services/WorkflowWSWorkflowManager", options);
  Logger.log(payload)
  Logger.log(response)
};

Can anyone look at my code and see any problems, or suggest any other alternatives? 谁能看我的代码,看看有什么问题,或提出其他建议?

I found the problem(s) in my original code. 我在原始代码中发现了问题。

  1. the 0x00 padding in DIME messages goes AFTER the data, not before. DIME消息中的0x00填充在数据之后而不是之前。
  2. Instead of a string, I need to attach the payload as a Byte[] array. 除了字符串以外,我还需要将有效负载附加为Byte []数组。 However, this can not be one where I concat 2 or more Byte[] arrays, as I think they are treated as a normal array. 但是,这不能是我连接2个或更多Byte []数组的地方,因为我认为它们被视为普通数组。 So I run it through Utilities.newBlob().getBytes() to make a new Byte[] array just in case. 因此,我通过Utilities.newBlob()。getBytes()运行它以创建一个新的Byte []数组,以防万一。 This is the final piece that made it go through. 这是完成它的最后一部分。

The correct code is below. 正确的代码如下。

function makeDimeHeader (position, isChunk, id, contentType, data) {
  // ref: http://xml.coverpages.org/draft-nielsen-dime-02.txt
  var bin = ""

  // version 1
  bin += "00001"

  if (position == "start") {
    bin += "10"
  } else if (position == "finish") {
    bin += "01"
  } else if (position == "middle") {
    bin += "00"
  } else if (position == "only") {
    bin += "11"
  } else {
    throw "Error: position must be 'start', 'only', 'middle', or 'finish'"
  }

  if (isChunk) {
    bin += "1"
  } else {
    bin += "0"
  }

  if (id.constructor !== Array) throw "Error: id must be a byte array"
  if (contentType.constructor !== Array) throw "Error: contentType must be a byte array"
  if (data.constructor !== Array) throw "Error: data must be a byte array"

  var textType = Utilities.newBlob(contentType).getDataAsString()

  if (textType.slice(0,4) == "http") {
    bin += "0010"
  } else if (contentType.length == 0) {
    bin += "0000"
  } else {
    bin += "0001"
  }

  // RESERVED
  bin += "0000"

  // OPTIONS LENGTH
  bin += "0000000000000000"

  bin += ("0000000000000000" + id.length.toString(2)).slice(-16)

  bin += ("0000000000000000" + contentType.length.toString(2)).slice(-16)

  bin += ("00000000000000000000000000000000" + data.length.toString(2)).slice(-32)

  var bytes = []
  for (var i = 0; i < bin.length/8; i++) {
    num = parseInt(bin.slice(i*8,(i*8)+8),2);
    // Byte[] is an Int8Array, not Uint8Array
    if (num > 127) num = num - 256
    bytes.push(num)
  }

  bytes = bytes.concat(id)
  // PADDING TO CLOSEST 4 BYTES
  if (id.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (id.length % 4)))

  bytes = bytes.concat(contentType)
  // PADDING TO CLOSEST 4 BYTES
  if (contentType.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (contentType.length % 4)))

  bytes = bytes.concat(data)
  // PADDING TO CLOSEST 4 BYTES
  if (data.length % 4 != 0) bytes = bytes.concat([0, 0, 0].slice(0, 4 - (data.length % 4)))

  return bytes

}

function testPost() {
  var file = UrlFetchApp.fetch("https://.../somedoc.xlsx");
  var fileBlob = file.getBlob();
  var fileBytes = fileBlob.getBytes();

  var contentType = 'application/dime';

  var data = Utilities.newBlob(
    "<?xml version=\"1.0\"?>\r\n" +
    "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:com=\"http://www.idiominc.org/com.idiominc.webservices.WorkflowWSWorkflowManager\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
    "<soapenv:Header/>" +
    "<soapenv:Body>" +
    "<com:createProjectGroup7_ soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
    "<token xsi:type=\"xsd:string\">0000000000</token>" +
    "<name xsi:type=\"xsd:string\">TEST NEW PROJECT</name>" +
    "<locales xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\">" +
    "<item type=\"xsd:string\">English (United States)</item>" +
    "<item type=\"xsd:string\">Korean</item>" +
    "</locales>" +
    "<attachedFile xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\">" +
    "<file type=\"xsd:string\">test.xlsx</file>" +
    "</attachedFile>" +
    "<client xsi:type=\"xsd:string\">TEST CLIENT</client>" +
    "<projectType xsi:type=\"xsd:string\">TEST PROJECT TYPE</projectType>" +
    "<customAisProperties xsi:type=\"data:stringArray\" soapenc:arrayType=\"xsd:string[]\" xmlns:data=\"http://webservices.idiominc.com/data\"/>" +
    "</com:createProjectGroup7_>" +
    "</soapenv:Body>" +
    "</soapenv:Envelope>").getBytes()

  var soapcontent = Utilities.newBlob(
    "http://schemas.xmlsoap.org/soap/envelope/"
    ).getBytes()

  var firstSoapDime = makeDimeHeader("start", false, [], soapcontent, data)

  var filecontent = Utilities.newBlob(
    "application/octet-stream"
    ).getBytes()

  var lastFileData = makeDimeHeader("finish", false, Utilities.newBlob("test.xlsx").getBytes(), filecontent, fileBytes)

  var payloadbytes = firstSoapDime.concat(lastFileData)

  var headers =
      {
        "SOAPAction" : ""
      };

  var payload = Utilities.newBlob(payloadbytes).getBytes();

  var options =
      {
        "method" : "post",
        "headers" : headers,
        "contentType": contentType,
        "contentLength": payloadbytes.length,
        "payload" : payload,
        "muteHttpExceptions" : true
      };

  var response = UrlFetchApp.fetch("https://<ourdomain>.sdlproducts.com/ws/services/WorkflowWSWorkflowManager", options);
  Logger.log(payload)
  Logger.log(response)
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM