【问题标题】:submit multiple attachment lines to Google DriveApp向 Google DriveApp 提交多个附件行
【发布时间】:2020-01-22 00:30:13
【问题描述】:

我正在尝试从每行末尾的输入字段提交多行数据以及附件。我为此使用了 Google App-Script Webapp。我成功地使用文本数据(例如日期、姓名、姓氏等)创建了一个对象数组,但似乎无法将附件作为对象的一部分发送。我做错了什么?

我还应该澄清一下,此代码不适用于一个或多个附件。我希望我可以一次发送多个附件分组(因此是对象数组)。

这是我在 HTML/Javascript 客户端的代码:

document.addEventListener("DOMContentLoaded", function() {
  document.getElementById("tripPost").addEventListener("click", addLine);
  document.getElementById("submitAll").addEventListener("click", addRecord);
});

//global variables for next functions
var submit = document.getElementById("tripPost");
var submittedTable = document.getElementById("submitted-data");
var mainEntry = document.getElementById("entry-table");
var submitAll = document.getElementById("submitAll");

submittedTable.addEventListener("click", addLine);
submittedTable.addEventListener("change", fileUpload);

function addLine() {
  document.getElementById("table-container").style.display = "block";

  var date = document.getElementById("date1").value;
  var efirst = document.getElementById("efirst").value;
  var elast = document.getElementById("elast").value;

  var row = document.createElement("tr");
  var col1 = document.createElement("td");
  col1.appendChild(document.createTextNode(date));
  col1.className = "postDateClass";
  var col2 = document.createElement("td");
  col2.appendChild(document.createTextNode(efirst));
  col2.className = "postEfirstClass";
  var col3 = document.createElement("td");
  col3.appendChild(document.createTextNode(elast));
  col3.className = "postElastClass";

  var col4 = document.createElement("td");

  row.appendChild(col1);
  row.appendChild(col2);
  row.appendChild(col3);
  row.appendChild(col4);

  submittedTable.appendChild(row);

  var uniqueID = "id" + new Date().getTime();
  var upload = document.createElement("input");
  upload.type = "file";
  upload.id = uniqueID;
  upload.name = "myReceipt";
  upload.className = "uploadClass";

  var label = document.createElement("label");
  label.innerHTML = "upload me please!";
  label.htmlFor = uniqueID;
  label.className = "custom-file-upload";

  var form = document.createElement("form");
  form.appendChild(upload);
  form.appendChild(label);

  col4.appendChild(form);
}

function fileUpload(e) {
  if (e.target.className === "uploadClass") {
    if (e.target.value) {
      var span = document.createElement("span");
      span.className = "uploadSpanText";
      span.innerHTML = e.target.value.match(/[\/\\]([\w\d\s\.\-\(\)]+)$/)[1];
      e.target.parentElement.appendChild(span);
      e.target.nextElementSibling.innerHTML = "uploaded!";
      e.target.nextElementSibling.style.border = "1px solid #a8e0b4";
      e.target.nextElementSibling.style.color = "#8bca9e";
    }
  }
}

function getFile(file) {
  return new Promise(resolve => {
    const fr = new FileReader();
    fr.onload = e => {
      const data = e.target.result.split(",");
      const obj = {
        fileName: file.name,
        mimeType: data[0].match(/:(\w.+);/)[1],
        data: data[1]
      };
      resolve(obj);
    };
    if (file) {
      fr.readAsDataURL(file);
    } else {
      reject("No File");
    }
  });
}

//gathers inputs and stores values in an object and runs the "addLine" function
async function addRecord(e) {
  var dateLines = document.querySelectorAll(".postDateClass");
  var eFirstLines = document.querySelectorAll(".postEfirstClass");
  var eLastLines = document.querySelectorAll(".postElastClass");
  var attachmentLines = document.querySelectorAll(".uploadClass");
  var mileageData = [];
  for (var i = 0; i < dateLines.length; i++) {
    var mileageLines = {};
    mileageLines.travelDate = dateLines[i].textContent;
    mileageLines.firstName = eFirstLines[i].textContent;
    mileageLines.lastName = eLastLines[i].textContent;
    mileageLines.receipt = await getFile(attachmentLines[i].parentNode);

    mileageData.push(mileageLines);
  }

  //send object to google. resets input elements
  google.script.run.userMileageSubmit(mileageData);
}

这是我正在使用的代码的 HTML。

<div id="entry-table">
     <table>
        <h3 style="text-align:left"><u><b>Enter mileage information below.</b></u><br></h3>
        <thead>
         <tr>
              <th >Date</th>
              <th >First:</th>
              <th >Last:</th>
         </tr>
         </thead>

         <tbody id="table-data">
           <tr>
            <td>
              <div class="disabled-results" id="date">
                 <input placeholder="Start Date" id="date1" type="text" class="datekeeper" required>
                <label for="date1" class="active">Date:</label>
              </div>
            <td>
              <div class="disabled-results">
                 <input id ="efirst" type="text" class="validate" >
                 <label for="efirst" class="active">First:</label>
              </div>
            </td>
            <td>
              <div class="disabled-results">
                 <input id ="elast" type="text" class="validate" >
                 <label for="elast" class="active">Last:</label>
              </div>
            </td>
            <td>
               <div id="status">
                  <button id="tripPost" class="waves-effect waves-light btn-small blue darken-3">Add Trip</button>
               </div>
            </td>
         </tr>
       </tbody>
      </table>
</div><!---CLOSE ROW ---> 

<div class="autocomplete" id="table-container" style=display:none>
       <table>
         <thead>
          <tr id="header-titles">
              <th >Date</th>
              <th >First:</th>
              <th >Last:</th>
              <th >Receipt </th>
          </tr>
        </thead>
        <form>
        <tbody class="form" id="submitted-data">
          <div>
              <p>Thank you!</p>
          </div>
          </form>
         </tbody>
      </table>
      <br><br>
   </div>

<center>
  <div class="row">    
    <button  id="submitAll" class="waves-effect waves-light btn btn-large blue darken-3"><i class="material-icons left">directions_car</i>Submit All Mileage!</button>          
  </div>
</center>

这里是 CSS

body {
    background-color: lightblue;
    margin-top: 80px;
    margin-bottom: 80px;
    margin-right: 80px;
    margin-left: 80px;    
    }

    h1{
    color: black;
    text-align: center;
    }
div.disabled-results{
  width: 175px;
  height: 80px;
  padding: 5px;
  margin: 5px;
  display: inline-table;
  box-sizing: border-box;
  text-align: center;
  }

input[type="file"]{
  display: none;
  }

  .custom-file-upload{
    border: 2px solid #000;
    width: 85px;
    display: inline-block;
    padding: 2px 1px;
    cursor: pointer;
    text-align: center;
  }

div.autocomplete{
width: 55px;
  height: 80px;
  padding: 5px;
  margin: 5px;
  display: inline-table;
  box-sizing: border-box;
  text-align: center;
  }

除了将每行中的附件(如果有的话)作为对象的一部分发送之外,我已经完成了所有其他工作。

我相信这是可以做到的。我尝试实现解决方案from this video,它向您展示了如何上传文件,但我不使用onclickthis.parentNode,因为我没有在选择文件后立即上传,而是在用户进行了许多条目。

任何有助于理解这应该如何工作的帮助将不胜感激。

谢谢。

【问题讨论】:

  • 我必须为我糟糕的英语水平道歉。我无法理解I should also clarify that this code won't work with either one or multiple attachments. I would hope that I could send multiple groupings of attachments (hence the array of objects) at one time.。这意味着输入标签的multiple?在您的情况下,我认为可能需要使用FileReader 检索该文件。但不幸的是,我不确定您的 HTML 的细节。那么为了正确理解你的情况,你能提供HTML吗?当然,请删除您的个人信息。
  • 我不确定这是否是您的问题,但如果您尝试通过 google.script.run 发送日期,请阅读 this,那么您将遇到问题。您可以将它们作为字符串发送,但不能作为 Date 对象发送。
  • @Tanaike 谢谢。是的,每一行都有一个输入标签。因此,如果用户使用 5 行,则将有 5 个附件。如果这回答了您的问题,请告诉我。
  • @Cooper 是的,我对日期格式没有问题。
  • 感谢您的回复。我不得不为我糟糕的英语水平道歉。不幸的是,从您的回复中,我看不到您实际 HTML 的愿景。

标签: javascript html google-apps-script


【解决方案1】:

这个修改怎么样?请认为这只是几个可能的答案之一。

很遗憾,在这种情况下,来自 HTML 端的文件对象无法作为 blob 直接发送到 Google Apps 脚本。因此,作为几种解决方法之一,在此修改中,检索到的文件被编码为 base64 数据并将其发送到 Google Apps 脚本。然后,在 Google Apps 脚本端,数据被解码并保存为文件。

请按如下方式修改您的脚本。

HTML 和 Javascript 方面:

请修改addRecord()并添加getFile()如下。

// Added
function getFile(file) {
  return new Promise((resolve, reject) => {
    const fr = new FileReader();
    fr.onload = e => {
      const data = e.target.result.split(",");
      const obj = {fileName: file.name, mimeType: data[0].match(/:(\w.+);/)[1], data: data[1]};
      resolve(obj);
    }
    if (file) {
      fr.readAsDataURL(file);
    } else {
      reject("No file");
    }
  });
}

async function addRecord(e) { // Modified
  var dateLines = document.querySelectorAll('.postDateClass');
  var attachmentLines = document.querySelectorAll('.uploadClass');

  var mileageData = [];
  for (var i=0; i<dateLines.length; i++){
    var mileageLines = {};
    mileageLines.firstName = document.getElementById("efirst").value; 
    mileageLines.lastName = document.getElementById("elast").value;
    mileageLines.date = dateLines[i].textContent;
    mileageLines.receipt = await getFile(attachmentLines[i].files[0]).catch(e => console.log(e));  // Modified
    mileageData.push(mileageLines); 
  };

  google.script.run.userMileageSubmit(mileageData);
};

Google Apps 脚本方面:

请修改userMileageSubmit()如下。

function userMileageSubmit(responses){
  responses.forEach(function(e) {
    var file = e.receipt;
    if (file) {
      var blob = Utilities.newBlob(Utilities.base64Decode(file.data), file.mimeType, file.fileName);
      var mainFolder = DriveApp.getFolderById('real-drive-link');
      var createFile = mainFolder.createFile(blob);
      var fileUrl = createFile.getUrl();
      Logger.log(fileUrl)
    }
  });

  // row.appendChild(col4)
  // submittedTable.appendChild(row)
}
  • 我无法理解row.appendChild(col4)submittedTable.appendChild(row)
  • 很遗憾,我无法理解您在userMileageSubmit() 的目标。因此,在此修改中,检索到的文件将保存到 Google Drive。并且可以在日志中看到创建文件的 URL。
    • 这里,请根据您的实际情况修改。
  • 我不确定real-drive-link。在这种情况下,请设置您要保存文件的文件夹 ID。

注意:

  • 在此修改中,假设您当前的addRecord() 有效。
  • 在此修改中,最大文件大小为 50 MB,因为 Google Apps 脚本的最大 blob 大小为 50 MB。请注意这一点。
  • 当上传大量文件时,处理时间会增加。请注意这一点。

参考资料:

【讨论】:

  • 感谢您的建议。他们看起来很有希望。我仍然收到一条错误消息,提示 TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'。知道这是怎么回事吗?再次感谢您的帮助!
  • @haby22 感谢您的回复。我带来的不便表示歉意。不幸的是,我无法复制您的情况。因为我无法理解attachmentLines[i].files[0] 的实际值。那么你能提供复制错误的示例脚本吗?当然,请删除您的个人信息。借此,我想确认这个问题。如果您能合作解决您的问题,我很高兴。
  • @haby22 对于给您带来的不便,我深表歉意。虽然我不确定您的实际脚本,但我在上面的脚本中添加了错误处理。请把上面修改过的脚本重新复制粘贴一遍,测试一下。那你能确认一下吗?如果没有创建文件,并且当您使用上述修改后的脚本时可以在控制台看到No file,则attachmentLines[i].files[0] 可能不是文件对象。到时候,请再检查一遍。
  • 再次感谢您。我已经隔离了可以帮助您复制错误的代码。请参阅上面的更新代码。再次感谢您!
  • @haby22 感谢您的回复和更新。如果您测试了更新后的脚本,您得到了什么结果?顺便问一下,关于您更新的脚本,函数addRecord 中的actualMilesLines 是什么?当您更新的脚本运行时,我们可以认为您最初的问题已经解决了吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-19
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多