Загрузка бинарных файлов сломана со вчерашнего дня, обходной путь? - PullRequest
1 голос
/ 03 марта 2020

Обновление: Я создал проблему: https://issuetracker.google.com/issues/150675170

Мое веб-приложение долгое время работало хорошо, но теперь внезапно загруженные PDF-файлы повреждены , Ниже приведен небольшой уменьшенный пример, который можно использовать для воспроизведения проблемы.

Глядя на содержимое загруженного файла, выглядит, что содержимое файла обрабатывается как текст, а несколько символов заменяются на EF BF BD, что является байтовой последовательностью UTF-8 для «ЗАМЕНА ХАРАКТЕРА» (U + FFFD).

Например, первые байты исходного файла PDF:

25 50 44 46 2D 31 2E 34 0A 25 E2 E3 CF D3 0A 31 39 | %PDF-1.4\n%âãÏÓ\n19

Он был загружен как:

25 50 44 46 2D 31 2E 34 0A 25 EF BF BD EF BF BD EF BF BD EF BF BD 0A 31 39 | %PDF-1.4\n%����\n19

Я не уверен, где сообщить об этом, я только надеюсь, что сотрудник Google увидит его и исправит.

Тем временем, возможно, у кого-то, кто знаком с Google Apps Script, есть идея обходного пути.

Ниже приведен небольшой сокращенный пример - разверните, загрузите двоичный файл, go на диск, найдите его в разделе «тест» », скачайте его, обратите внимание, что он поврежден.

HTML шаблон, имя файла test_form.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>

<form id="test-form">
    <input type="file" id="test-file" name="test-file">
    <button id="submit-button" type="submit">Upload</button>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
    (function () {
        'use strict';

        $('#test-form').submit(function (e) {
            e.preventDefault(); // prevent form from submitting

            google.script.run
                .withFailureHandler(fileUploadedFailure)
                .withSuccessHandler(fileUploaded)
                .uploadFilesFrame(this);
        });

        function fileUploaded(status) {
            alert(status);
        }

        function fileUploadedFailure(error) {
            alert('Failed: ' + error.message);
        }
    })();
</script>
</body>
</html>

Код сервера, имя файла Code.gs:

var rootFolderName = "test";

function doGet(e) {
  var template = HtmlService.createTemplateFromFile('test_form');
  return template.evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

function uploadFilesFrame(form) {
  try {
    var fileBlob = form['test-file'];

    var rootFolder = DriveApp.getFoldersByName(rootFolderName);
    if (rootFolder.hasNext()) {
      rootFolder = rootFolder.next();
    } else {
      rootFolder = DriveApp.createFolder(rootFolderName);
    }

    var file = rootFolder.createFile(fileBlob);

    return JSON.stringify({"status": 'ok', "msg": file.getId()});
  } catch (error) {
    return JSON.stringify({"status": 'error', "data": error.stack});
  }
}

Ответы [ 2 ]

2 голосов
/ 03 марта 2020

Один обходной путь: base64-кодирует его на стороне клиента, затем base64-декодирует его на сервере. Тогда содержимое не облажается. Вот пример:

HTML шаблон, имя файла test_form.html:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>

<form id="test-form">
    <input type="file" id="test-file" name="test-file">
    <input type="hidden" id="test-file2" name="test-file2">
    <input type="hidden" id="test-file-name" name="test-file-name">
    <button id="submit-button" type="submit">Upload</button>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
    (function () {
        'use strict';

        $('#test-form').submit(function (e) {
            var thisForm = this;
            e.preventDefault(); // prevent form from submitting

            var reader = new FileReader();
            reader.onload = function (event) {
                var result = event.target.result;
                var base64 = result.substr(result.indexOf(',') + 1);
                $('#test-file2').val(base64);

                var filename = $('#test-file').val().split('\\').pop();
                $('#test-file-name').val(filename);

                $('#test-file').prop('disabled', true);

                google.script.run
                    .withFailureHandler(fileUploadedFailure)
                    .withSuccessHandler(fileUploaded)
                    .uploadFilesFrame(thisForm);
            };
            reader.onerror = function (event) {
                alert("ERROR: " + event.target.error.code);
            };
            reader.readAsDataURL(document.getElementById('test-file').files[0]);
        });

        function fileUploaded(status) {
            alert(status);
        }

        function fileUploadedFailure(error) {
            alert('Failed: ' + error.message);
        }
    })();
</script>
</body>
</html>

Код сервера, имя файла Code.gs:

var rootFolderName = "test";

function doGet(e) {
  var template = HtmlService.createTemplateFromFile('test_form');
  return template.evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

function uploadFilesFrame(form) {
  try {
    var fileBlob = Utilities.newBlob(Utilities.base64Decode(form['test-file2']), 'application/octet-stream', form['test-file-name']);

    var rootFolder = DriveApp.getFoldersByName(rootFolderName);
    if (rootFolder.hasNext()) {
      rootFolder = rootFolder.next();
    } else {
      rootFolder = DriveApp.createFolder(rootFolderName);
    }

    var file = rootFolder.createFile(fileBlob);

    return JSON.stringify({"status": 'ok', "msg": file.getId()});
  } catch (error) {
    return JSON.stringify({"status": 'error', "data": error.stack});
  }
}
0 голосов
/ 03 марта 2020

Поскольку вы запрещаете поведение по умолчанию для отправки, как насчет того, чтобы попробовать это следующим образом:

Я на самом деле не решаюсь говорить вам об этом прямо сейчас, потому что я подозреваю, что вы знаете об этом больше, чем я. Но чтобы быть полезным и, возможно, выглядеть глупо, я предлагаю вам попробовать это так.

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<form>
    <input type="file" id="test-file" name="test-file">
    <input type="button" value="Submit" onClick="this.parentNode" />
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
    function uploadFile(form) {
      google.script.run
      .withFailureHandler(fileUploadedFailure)
      .withSuccessHandler(fileUploaded)
      .uploadFilesFrame(form);
    }
    function fileUploaded(status) {
      alert(status);
    }
    function fileUploadedFailure(error) {
       alert('Failed: ' + error.message);
     }

</script>
</body>
</html>
...