如何在浏览器中读取本地文本文件?

533

我正在尝试通过创建一个函数来实现一个简单的文本文件阅读器,该函数接受文件路径并将每行文本转换为字符数组,但它不起作用。

function readTextFile() {
  var rawFile = new XMLHttpRequest();
  rawFile.open("GET", "testing.txt", true);
  rawFile.onreadystatechange = function() {
    if (rawFile.readyState === 4) {
      var allText = rawFile.responseText;
      document.getElementById("textSection").innerHTML = allText;
    }
  }
  rawFile.send();
}

这里出了什么问题?

在从以前的修订版稍微更改代码后,仍然似乎无法正常工作,现在它给我一个XMLHttpRequest异常101。

我已在Firefox上测试过,它可以正常工作,但在Google Chrome上它只是不起作用,并且一直给我Exception 101。我如何使它不仅在Firefox上工作,而且在其他浏览器上(特别是Chrome)也能正常工作?


具体发生了什么?数组里面是空的吗?还是只有“错误”的东西...? - PinkElephantsOnParade
你是在本地机器上进行测试吗?请确保测试 status0200 - Jeffrey Sweeney
1
@JeffreySweeney 是的,我正在本地机器上测试这个。我已经将文本文件存储在与JavaScript和HTML相同的位置。 - Danny
24个回答

6

这个问题可能有点老,但我们需要澄清两个主要的想法。我们是想一次性读取整个文件呢?还是逐行读取?

Teo,我想获取整个文件并稍后处理它。

好的,这很容易。我们只需要调用 Blob.text() 方法(记住,此方法假定文件编码为 UTF-8),并像这样处理文件:

const $output = document.getElementById('output')
const $file = document.getElementById('file')
const fetchFile = async e => {
  const [file] = e.target.files
  const text = await file.text()
  $output.textContent = text
}

$file.onchange = fetchFile
<input id='file' type='file' accept='text/plain'><br>
<pre id='output'>...</pre>

逐行处理呢?这可行吗?

没问题,年轻的帕达万,这也是可以的。我们只需要调用 String.split() 方法将文本分割成一行行的数组即可,像这样:

const $output = document.getElementById('output')
const $file = document.getElementById('file')
let count
const fetchFile = async e => {
  const [file] = e.target.files
  if (!file) return
  count = 0
  const text = await file.text()
  $output.textContent = text

  const lines = text.split(/\r?\n/gm)
  for (const line of lines) {
    if (line) count++
  }
  console.log({count})
}

$file.onchange = fetchFile
<input id='file' type='file' accept='text/plain'><br>
<pre id='output'>...</pre>


迄今为止最佳答案!谢谢。 - Abednego Nasila
@Teocci 不错的例子,我想了解这个例子中为什么没有发生CORS违规? - LXSoft
CORS策略是一项安全功能,防止浏览器向其他服务器发起跨域请求。 该代码不违反CORS策略,因为文件是从本地文件系统获取的,而不是从远程服务器获取的。 - Teocci

5
这可能会有所帮助,
    var xmlhttp = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");

    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            alert(xmlhttp.responseText);
        }
    }

    xmlhttp.open("GET", "sample.txt", true);
    xmlhttp.send();

3

由于同源策略,Chrome不支持本地AJAX调用。

在Chrome中的错误信息如下: “协议方案(protocol schemes):http、data、chrome、chrome-extension、https不支持跨域请求。”

这意味着,Chrome为每个域名创建了一个虚拟磁盘,用于保存使用http/https协议提供的该域名服务的文件。任何访问此虚拟磁盘之外的文件都受到同源策略的限制。AJAX请求和响应发生在http/https上,因此对于本地文件无法工作。

Firefox没有这样的限制,因此您的代码将在Firefox上正常工作。但是,Chrome也有解决方法:请参见此处


你的“看这里”链接已经失效。 - isherwood

2

在 JavaScript 中获取本地文件数据(data.js)的加载:

function loadMyFile(){
    console.log("ut:"+unixTimeSec());
    loadScript("data.js?"+unixTimeSec(), loadParse);
}
function loadParse(){
    var mA_=mSdata.split("\n");
    console.log(mA_.length);
}
function loadScript(url, callback){

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}
function hereDoc(f) {
  return f.toString().
      replace(/^[^\/]+\/\*![^\r\n]*[\r\n]*/, "").
      replace(/[\r\n][^\r\n]*\*\/[^\/]+$/, "");
}
function unixTimeSec(){
    return Math.round( (new Date()).getTime()/1000);
}

data.js文件内容如下:

var mSdata = hereDoc(function() {/*!
17,399
1237,399
BLAHBLAH
BLAHBLAH
155,82
194,376
*/});

动态的unix时间戳查询字符串可以防止缓存。
AJ在web http://中工作。

为什么不使用ES6模板字面量语法来处理多行字符串?(请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) - Sapphire_Brick

2

除了上面的答案之外,以下修改后的解决方案对我有用。

<input id="file-upload-input" type="file" class="form-control" accept="*" />

....

let fileInput  = document.getElementById('file-upload-input');
let files = fileInput.files;

//Use createObjectURL, this should address any CORS issues.
let filePath = URL.createObjectURL(files[0]);

....

function readTextFile(filePath){
    var rawFile = new XMLHttpRequest();
    rawFile.open("GET", filePath , true);
    rawFile.send(null);

    rawFile.onreadystatechange = function (){
        if(rawFile.readyState === 4){
            if(rawFile.status === 200 || rawFile.status == 0){
                var allText = rawFile.responseText;
                console.log(allText);
            }
        }
    }     
}

2
function readTextFile(file) {
    var rawFile = new XMLHttpRequest(); // XMLHttpRequest (often abbreviated as XHR) is a browser object accessible in JavaScript that provides data in XML, JSON, but also HTML format, or even a simple text using HTTP requests.
    rawFile.open("GET", file, false); // open with method GET the file with the link file ,  false (synchronous)
    rawFile.onreadystatechange = function ()
    {
        if(rawFile.readyState === 4) // readyState = 4: request finished and response is ready
        {
            if(rawFile.status === 200) // status 200: "OK"
            {
                var allText = rawFile.responseText; //  Returns the response data as a string
                console.log(allText); // display text on the console
            }
        }
    }
    rawFile.send(null); //Sends the request to the server Used for GET requests with param null
}

readTextFile("text.txt"); //<= Call function ===== don't need "file:///..." just the path 
- 从JavaScript中读取文件文本
- 使用JavaScript从文件中记录文本
- Google Chrome和Mozilla Firefox

在我的情况下,我有这种文件结构:enter image description here

控制台日志结果:
enter image description here


2
以下是显示的错误: 已被CORS策略阻止:仅支持协议方案的跨域请求:http,data,chrome,chrome-extension,https。无法从源'null'访问'file:///C:/{myLocalPath}PropertiesFile.txt'。 - Kumar Kartikeya

2

如果您想提示用户选择文件并读取其内容:

// read the contents of a file input
const readInputFile = (inputElement, callback) => {
  const reader = new FileReader();
  reader.onload = () => {
    callback(reader.result)
  };
  reader.readAsText(inputElement.files[0]);
};
// create a file input and destroy it after reading it
const openFile = (callback) => {
  var el = document.createElement('input');
  el.setAttribute('type', 'file');
  el.style.display = 'none';
  document.body.appendChild(el);
  el.onchange = () => {readInputFile(el, (data) => {
    callback(data)
    document.body.removeChild(el);
  })}
  el.click();
}

使用方法:

// prompt the user to select a file and read it
openFile(data => {
    console.log(data)
  })

2

如何读取本地文件?

通过使用loadText()函数,您可以加载一个文件,然后JS会异步等待文件被读取和加载完毕,之后它将执行readText()函数,允许您继续正常的JS逻辑(在loadText()函数中也可以编写try catch块以防出现任何错误),但是对于这个例子,我将其保持最小化。

async function loadText(url) {
    text = await fetch(url);
    //awaits for text.text() prop 
    //and then sends it to readText()
    readText(await text.text());
}

function readText(text){
    //here you can continue with your JS normal logic
    console.log(text);
}

loadText('test.txt');

1
似乎你患上了“函数病”。 - Sapphire_Brick

1
<html>
<head>
    <title></title>
    <meta charset="utf-8" />
    <script src="https://code.jquery.com/jquery-1.10.2.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {            
                $.ajax({`enter code here`
                    url: "TextFile.txt",
                    dataType: "text",
                    success: function (data) {                 
                            var text = $('#newCheckText').val();
                            var str = data;
                            var str_array = str.split('\n');
                            for (var i = 0; i < str_array.length; i++) {
                                // Trim the excess whitespace.
                                str_array[i] = str_array[i].replace(/^\s*/, "").replace(/\s*$/, "");
                                // Add additional code here, such as:
                                alert(str_array[i]);
                                $('#checkboxes').append('<input type="checkbox"  class="checkBoxClass" /> ' + str_array[i] + '<br />');
                            }
                    }                   
                });
                $("#ckbCheckAll").click(function () {
                    $(".checkBoxClass").prop('checked', $(this).prop('checked'));
                });
        });
    </script>
</head>
<body>
    <div id="checkboxes">
        <input type="checkbox" id="ckbCheckAll" class="checkBoxClass"/> Select All<br />        
    </div>
</body>
</html>

1

这个功能是为浏览器和打开文件选择对话框而设计的,用户选择文件后会将文件作为二进制文件读取并调用回调函数以读取数据:

function pickAndReadFile(callback) {
    var el = document.createElement('input');
    el.setAttribute('type', 'file');
    el.style.display = 'none';
    document.body.appendChild(el);
    el.onchange = function (){
        const reader = new FileReader();
        reader.onload = function () {
            callback(reader.result);
            document.body.removeChild(el);
        };
        reader.readAsBinaryString(el.files[0]);
    }
    el.click();
}

然后像这样使用:

pickAndReadFile(data => {
  console.log(data)
})

你不应该再使用readAsBinaryString了... - Endless

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接