【问题标题】:file input files not read onChange on mobile文件输入文件未在移动设备上读取 onChange
【发布时间】:2018-08-19 21:32:10
【问题描述】:

我正在 React 中构建一个拼图应用程序,允许用户上传他们自己的拼图。这在网络上运行良好(用户单击输入的标签并打开一个对话框。当用户选择一个文件时,onChange 事件被触发),但在移动设备上,或者至少在 Android 上的 Chrome 上,文件不是读...

这是声明输入的地方:

        <div className="file-input-wrapper">
            <label for="puzzleUpload" className="button-dark">Upload Puzzle(s)</label>
            <input type="file"
                   accept="application/json"
                   multiple
                   id="puzzleUpload"
                   onChange={handleFiles}/>
        </div>

这就是handleFiles() 方法

// when a file is uploaded, this checks to see that it's the right type, then adds it to the puzzle list
const handleFiles = () => {
    var selectedFiles = document.getElementById('puzzleUpload').files;


    // checks if the JSON is a valid puzzle
    const validPuzzle = (puzzle) => {
        let keys = ["name", "entitySetID", "logic", "size"];
        return keys.every((key) => {return puzzle.hasOwnProperty(key)});
    };


    const onLoad = (event) => {
        let puzzle = JSON.parse(event.target.result);
        if(validPuzzle(puzzle)) {
            appendPuzzleList(puzzle);
        }
        else {
            console.log("JSON file does not contain a properly formatted Logike puzzle")
        }
    };

    //checks the file type before attempting to read it
    for (let i = 0; i < selectedFiles.length; i++) {
        if(selectedFiles[i].type === 'application/json') {
            //creates new readers so that it can read many files sequentially.
            var reader = new FileReader();
            reader.onload = onLoad;

            reader.readAsText(selectedFiles[i]);
        }
    }
};

可以在http://logike.confusedretriever.com 找到具有最新代码的工作原型,并且可以使用应用中的构建器快速编写兼容的 JSON。

过去一个半小时我一直在寻找解决方案,但空手而归,因此我们将不胜感激!我阅读了 FileReader 文档,似乎所有内容都受支持,所以我有点难过。

有趣的是,文件被选中(一旦选中,您可以在输入的丑陋默认版本中看到文件名,但我通过 CSS 将其隐藏),所以我很想实现一个仅限移动设备的按钮来触发事件,如果没有更合法的解决方案...

【问题讨论】:

    标签: javascript reactjs input


    【解决方案1】:

    Chrome 使用操作系统的已知 MIME 类型列表。
    我猜Android不知道"application/json",至少,不会将.json扩展名映射到这个MIME类型,这意味着当你在这个浏览器中上传你的文件时,你不会有正确的@ 987654326@ 属性集,而是设置为空字符串("")。

    但无论如何,你永远不应该相信这个 type 属性。

    因此,您始终可以避免使用一些泛型类型,例如 image/*video/*,但了解它是否是有效 JSON 文件的唯一可靠方法是实际读取文件中包含的数据。

    但我知道,如果您的用户提供了一个大文件(如视频),您不想启动此操作。

    如果您知道生成的文件可能来自哪个范围,一个简单的解决方案可能是检查 size 属性。

    一个不太简单但不是那么难的解决方案是在生成的文件中添加一个magic number(又名File Signature)(如果您的应用是处理这些文件的唯一方法)。

    那么你只需要在读取整个文件之前检查这个幻数:

    // some magic-number (here "•MJS")
    const MAGIC_NB = new Uint8Array([226, 128, 162, 77, 74, 83]);
    
    // creates a json-like File, with our magic_nb prepended
    function generateFile(data) {
      const str = JSON.stringify(data);
      const blob = new Blob([MAGIC_NB, str], {
        type: 'application/myjson' // won't be used anyway
      });
      return new File([blob], 'my_file.json');
    }
    
    // checks whether the provided blob starts with our magic numbers or not
    function checkFile(blob) {
      return new Promise((res, rej) => {
        const reader = new FileReader();
        reader.onload = e => {
          const arr = new Uint8Array(reader.result);
          res(!arr.some((v, i) => MAGIC_NB[i] !== v));
        };
        reader.onerror = rej;
        // read only the length of our magic nb
        reader.readAsArrayBuffer(blob.slice(0, MAGIC_NB.length));
      });
    }
    
    function handleFile(file) {
      return checkFile(file).then(isValid => {
        if (isValid) {
          return readFile(file);
        } else {
          throw new Error('invalid file');
        }
      });
    }
    
    function readFile(file) {
      return new Promise((res, rej) => {
        const reader = new FileReader();
        reader.onload = e => res(JSON.parse(reader.result));
        reader.onerror = rej;
        // don't read the magic_nb part again
        reader.readAsText(file.slice(MAGIC_NB.length));
      });
    }
    
    const my_file = generateFile({
      key: 'value'
    });
    handleFile(my_file)
      .then(obj => console.log(obj))
      .catch(console.error);

    同样请注意,并非所有浏览器都接受 accept 属性的所有方案,并且您可能希望使用简单的扩展名将 MIME 符号加倍(无论如何,即使是 MIME 也仅针对此进行检查扩展名)。

    【讨论】:

    • 还没有尝试过实现幻数(在应用程序中还不需要这种级别的健壮性,但我肯定会将它添加到我的待办事项列表中。)现在我只是检查扩展名是否为 .json 和大小,这对于我现在正在做的事情并解决了问题......我试图阅读application/json MIME 类型的移动支持,但找不到任何东西... MDN 文档似乎没有提到任何 android 特定缺乏对它或 accept 属性的支持,所以我很好奇你在哪里找到这个信息。
    • @JCLaHoot 我通过尝试和检查控制台发现,已经知道 chrome 没有自己的扩展映射,不像 FF。看来我的观察是正确的:stackoverflow.com/questions/44667125/…
    猜你喜欢
    • 2021-01-29
    • 1970-01-01
    • 2011-06-17
    • 2017-07-10
    • 2013-09-15
    • 1970-01-01
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    相关资源
    最近更新 更多