javascript - 向 Google DriveApp 提交多个附件行

标签 javascript html google-apps-script

我正在尝试从每行末尾的输入字段提交带有附件的多行数据。我正在为此使用 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 因为我不会在选择文件后立即上传,而是执行当用户输入大量内容时批量上传。

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

谢谢。

最佳答案

这个修改怎么样?请将此视为几个可能答案之一。

遗憾的是,在这种情况下,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)subscribedTable.appendChild(row)
  • 遗憾的是,我无法理解您在 userMileageSubmit() 上的目标。因此,在本次修改中,检索到的文件将保存到 Google Drive。并且可以在日志中看到创建的文件的URL。
    • 这里,请根据您的实际情况进行修改。
  • 我不确定real-drive-link。在这种情况下,请设置要保存文件的文件夹 ID。

注意:

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

引用文献:

关于javascript - 向 Google DriveApp 提交多个附件行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59851104/

相关文章:

google-apps-script - 如何使用 Google-apps-script 获取工作表中的目标单元格行号

javascript - 为什么我的 Scriptlet 无法正常工作 html Apps 脚本

javascript - 以编程方式声明基于索引数组的变量(使用谷歌站点列表页面)

JavaScript、Tampermonkey GM_setValue 和 GM_getValue

javascript - 如何在 angular.js 中获取复选框的值

HTML/CSS 嵌套混淆

Javascript循环/递增值最大为55的html代码

javascript - 网址末尾的广告 anchor

javascript - 以 "For Loop"上传到 Google Cloud Storage(异步)

javascript - jquery UI 自动完成字母顺序