使用 JavaScript 将 tsv 文件上传按钮添加到 Jupyter Notebook

Add tsv file upload button to Jupyter Notebook using JavaScript

所以我正在使用 Jupyter 4.x 和 python 3.5,试图 "upload" 一个 .tsv,但实际上只是试图将它捕获为一个字符串然后使用 setTimeout(function(){IPython.notebook.kernel.execute("stringData=StringIO(+"fr.result")");},5000);

FileReader() 对象有时间完成二进制字符串转换,然后将字符串保存到 python 变量中。我通过在控制台中打印整个 .tsv 文件来测试 Filereader() 是否正常工作。但是,出于某种原因,python side.Here 中的 stringData 仍未定义 javascript 单元格:

%%HTML
<input type="file" id="CSVFileInput" onchange="handleFiles(this.files)" value="upload csv">

<script>
var inputElement=document.getElementById('CSVFileInput');
function handleFiles() {
    var file = inputElement.files[0];
    var fr = new FileReader();
    fr.readAsText(file);
    var outputString=fr.result;
    var command = "dataString ='"+outputString+"'";  
    setTimeout(function(){
        IPython.notebook.kernel.execute(command);}
        ,5000);
}
inputElement.addEventListener("change", handleFiles, false);
</script>

然后在下一个单元格中我测试输出并得到 NameError 因为 dataString 未定义,这是下一个单元格:

dataString

此外,我对 javascript 有点陌生,所以欢迎任何建议,我只是认为这是简单的方法。请问?当然,非常感谢!

FileReader 方法是异步的,因此在您尝试将 outputString 设置为结果时没有加载数据。

处理这个问题的正确方法是使用load事件处理程序,因此可以将代码修改为以下内容,而不是使用不可靠的异步处理setTimeout()

function handleFiles() {
  var file = this.files[0];              // "this" is the calling element
  var fr = new FileReader();
  fr.onload = function() {
    var outputString = this.result;      // here the data is ready. Now "this" = fr
    var command = "dataString ='" + outputString + "'";  
    IPython.notebook.kernel.execute(command);
  };
  fr.readAsText(file);                   // invoked asynchronously
}

同时删除 HTML 中的内联 JavaScript:

<input type="file" id="CSVFileInput" onchange="handleFiles(this.files)" value="upload csv">

<input type="file" id="CSVFileInput" title="upload csv">

value在输入为type=file时无效,请改用title)。然后使用以下代码处理事件(在 DOM 加载后):

document.getElementById("CSVFileInput").addEventListener("change", handleFiles);

function handleFiles() {
  var file = this.files[0];              // "this" is the calling element
  var fr = new FileReader();
  fr.onload = function() {
    var outputString = this.result;      // here the data is ready. Now "this" = fr
    var command = "dataString ='" + outputString + "'";  
    //IPython.notebook.kernel.execute(command);
    console.log("Loaded file. Command:", command);
  };
  fr.readAsText(file);                   // invoked asynchronously
}

document.getElementById("CSVFileInput").addEventListener("change", handleFiles);
<input type="file" id="CSVFileInput" title="upload csv">

所以,@K3N 确实给了我一个有价值的难题和异步函数的教训。然而,主要问题是 python 无法识别它从 javascript 接收到的字符串输入,所以我想与大家分享我的旅程。我最终将字符串转换为 2d javascript 数组,然后我 shift() 关闭列名的第一行,转置剩余的行,并清除英语中所有阻止它的愚蠢内容工作(引号和撇号)。现在我可以 pd.DataFrame(dict(zip(colNames,cols))) 和 运行 我 运行 在同一个 .tsv 上的所有计算,当我从我的文件系统读取它时。这是完整的脚本,基本上是 @K3N 向我展示的修复以及 pythonify(arr) 函数:

    <input type="file" id="TSVFileInput" title="upload tsv">
function handleFiles() {
    //read in file and instantiate filereader
    let file = this.files[0];// "this" is the calling element
    let fr = new FileReader();

    fr.onload = function() {        
        //split on row delimeter (CRLF)
        let outputBuffer = this.result.split("\r\n");  
        let command;
        // split outputBuffer into 2d array
        outputBuffer= outputBuffer.map(line => line.split("\t"));
        //pop names row from output Buffer
        let names=outputBuffer.shift();
        //optimized transpose   
        outputBuffer=outputBuffer.reduce(
            (temp,row) => 
                row.map((element,i) => 
                    (temp[i] || []).concat(element))
            ,[] //initializes temp
        );
        //build python command 
        command="colNames ="+pythonify(names);
        //send command to notebook kernel
        IPython.notebook.kernel.execute(command);    
        //loop appends columns on python side
        for(let i=0 ; i< outputBuffer.length ; i++){  
            command="cols.append("+pythonify(outputBuffer[i])+")";    
            IPython.notebook.kernel.execute(command);   //send command to kernel
        } 
    }; //end fr.onload()
    fr.readAsText(file); // invoked asynchronously, triggers fr.onload
}//end handleFiles()

function pythonify (arr){    
//turns javascript array into string representation of python list
    let out= '[';
    for(let i=0 ; i<arr.length ; i++){
        var element=arr[i];
        //format double and single quotes
        element=element.replace(/\"/g,'\"').replace(/'/g,"\'");
        //use python raw string
        out+='r"'+element+'"';
        if(i<arr.length-1){ //skip last comma
            out+=',';
        }
    }
    out+=']';
    return out;
}// end pythonify(arr)

document.getElementById("CSVFileInput").addEventListener("change", handleFiles);