Размер и содержимое входного файла не обновляются на macOS

Я написал небольшой веб-инструмент, который использует ввод файла для чтения постоянно меняющегося файла. Пользователь выбирает его вручную (один раз!) И дорожки JavaScript, когда он был изменен (время последнего изменения файла и размер файла). Если он изменился, он снова считывает содержимое файла.

Это отлично работает во всех браузерах в Windows. Но на macOS (проверено в Safari 10.1.2 и Firefox 51.0.1) обновляется только последнее время модификации. Размер файла не обновляется, и кажется, что содержимое файла больше не читается. Поэтому я не могу отслеживать изменения файлов в браузерах на macOS.

Но почему? Это ограничение безопасности в macOS?

Проконсультируйтесь со следующим фрагментом. Выберите файл (например, текстовый файл), см. Последнюю измененную метку времени и размер файла, затем измените файл и посмотрите еще раз, если размер изменился. В macOS размер файла не изменяется.

Нет jQuery, пожалуйста.

window.addEventListener('load', function() {
  window.setInterval(function() {
    var logFile = document.querySelector('#file').files[0];
    if (logFile) {
      document.querySelector('#info').innerHTML = '<br/>' +
        (new Date()).toString() + '<br/>Last modified: ' +
        logFile.lastModified +
        '<br/>Size: ' +
        logFile.size;
    }
  }, 1000);
});
#info {
  font-family: Courier;
  font-size: 0.9em;
}
<!DOCTYPE html>
<input type="file" id="file" />
<p id="info"></p>

Ответ 1

Я не думаю, что это мера безопасности, а не просто показатель эффективности.
Когда у них будет информация о метаданных файла, они больше не будут запрашивать ее при дальнейшем получении => меньшего доступа к пользовательскому диску.
Честно говоря, я даже немного удивлен, что в Windows они каждый раз запрашивают эти метаданные.

Чтобы обойти это, это непростая задача, и я бы пересмотрел потребности для этого, и если обмен сообщениями не может быть выполнен на другом уровне (например, из процесса, который модифицирует файлы).

Действительно, Firefox и Safari не ведут себя одинаково в этой ситуации:

  • Кажется, что Safari создает blobURI, указывающий на файл, как только вы его выбрали, формирует ввод и всегда использует его, когда вы пытаетесь получить к нему доступ позже (например, из FileReader). Реальная проблема здесь в том, что мы не можем получить новую версию на диске, потому что по каким-то причинам этот blobURI не кажется реальным указателем на диск... Но если мы хотим только обнаружить изменения файла, то это хорошо для нас, так как нам нужно будет проверить только событие FileReader.onerror, которое будет работать даже с File.slice(0,1) (т.е. минимальным File.slice(0,1) выводом).

  • Firefox, с другой стороны, вызовет ошибку FileReader, только если размер кэшированных метаданных не совпадает с размером, который был прочитан. Это означает, что вам нужно будет прочитать весь файл при каждой проверке, и, если ошибка не возникла, дважды проверьте, что данные на самом деле одинаковы... Но в этом браузере вы все равно можете извлечь фактический файл на диске из AJAX с использованием blobURI, чтобы получить последнюю версию.

Ответ 2

Я помню, что у меня была проблема с чтением ввода файла снова и снова. по-видимому, он пометил файл как измененный, и у меня возникла проблема с использованием FileReader в файле после его изменения. но вокруг есть способ


Существует способ, которым вы можете использовать для чтения содержимого папки. После того, как вы введете каталог с помощью перетаскивания или выбора папки из ввода файла каталога, вы можете снова и снова перебирать каждый файл, чтобы увидеть, что-то изменилось.

по какой-то причине он не работал в SO из-за ограничения песочницы, поэтому я создал jsfiddle и

function traverseFileTree(entery, path) {
  path = path || ""

  if (entery.isFile) {
    // Get file
    entery.file(file => {
        setInterval(() => {
          entery.file(file => {
            console.log(file.lastModifiedDate, file.size, path)
          })
        }, 1000)
    })
  } else if (entery.isDirectory) {
    // Get folder contents
    var dirReader = entery.createReader()
    dirReader.readEntries(entries => {
      for (let entery of entries) {
        traverseFileTree(entery, path + entery.name + '/')
      }
    }, console.error)
  }
}

var dropzone = document.getElementById('dropzone')

dropzone.addEventListener("drop", function(event) {
  event.preventDefault()

  const items = event.dataTransfer.items

  for (item of items) {
    // webkitGetAsEntry is where the magic happens
    const entery = item.webkitGetAsEntry()

    if (entery) {
      traverseFileTree(entery)
    }
  }
}, false)

// Required for drop event to event do what we want
dropzone.ondragover = event => {
  event.preventDefault()
  return false
}
#dropzone{
  background: black;
  height: 30px;
  color: white;
  padding: 10px;
}
<div id="dropzone">
  drop a directory here
</div>