Для загрузки информации о торговых точках в наш логистический SaaS-сервис «Муравьиная логистика» из Excel я решил использовать web-браузер. Обычно проще загрузить файл на сервер и с помощью любой библиотеки залить в БД. Но мне было интересно загрузить его построчно для контроля целостности каждой строки на клиенте, ну и, конечно, опробовать так всеми рекламируемое HTML5 FileAPI и Drag and Drop.
Книга Exсel – это ZIP архив с каталогами и файлами XML в формате Open XML. Парсить XML отлично умеет jQuery, а вот зиппить нет. Для этого на просторах сети была найдена библиотека zip.js, которая прекрасно справилась с поставленной задачей.
Итак, попробуем посмотреть, что же находится внутри архива:
<div class="main">
<progress id="progress"></progress>
<div class="filedrag" id="comps">Перетащите файл <span class="red">сюда</span></div>
<div class="result"></div>
</div>
var c = document.getElementById("comps"),
FileDragHover = function (e) {
e.stopPropagation();
e.preventDefault();
if(e.target.id==='comps')
e.target.className = (e.type == "dragover" ? "filedrag hover" : "filedrag");
else
c.className = (e.type == "dragover" ? "filedrag hover" : "filedrag");
}
c.addEventListener("drop", function(e){
e.preventDefault();
c.className = "filedrag";
var files = e.target.files || e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
if(f.name.toLowerCase().indexOf('xlsx')<=0) {
alert('Это не файл Excel');
} else {
zip.createReader(new zip.BlobReader(f), function(reader) {
// Получаем все файлы архива
reader.getEntries(function(entries) {
// В консоли появятся все внутренности архива Excel
console.info(entries)
return false;
});
}, function(error) {
alert("Ошибка: " + error)
});
}
}
return false;
}, false);
c.addEventListener("dragover", FileDragHover, false);
c.addEventListener("dragleave", FileDragHover, false);
Результат можно посмотреть тут. Скачайте пример файла и перетащите его на форму.
В консоли появится список всех файлов архива книги Excel. Среди свойств объектов, появившихся в консоли, есть filename, по нему-то мы и будем искать необходимые нам файлы XML.
Нам понадобятся два файла из архива:
- import.xlsxxlworksheetssheet[N].xlsx
- import.xlsxxlsharedStrings.xml
где:
sheet[N].xlsx — собственно лист Excel, N — его внутренний номер в книге.
sharedStrings.xml — ассоциативный массив строк, словарь листа.
Отфильтруем только нужные для нас файлы:
// Получаем все файлы архива
reader.getEntries(function(entries) {
var a=[],st;
for(var i in entries){
var e=entries[i];
var fn=e.filename.toLowerCase();
if(fn.indexOf("sheet")>0){
a.push(e);
}
else if(fn.indexOf("sharedstring")>0){
st=e;
}
}
// Массив всех листов книги Excel
console.info(a)
// Ассоциативный массив строк
console.info(st)
return false;
});
Результат можно посмотреть тут, закинув файл и посмотрев в консоль.
Далее нам необходимо извлечь данные простыми селекторами, для словаря строк это — st t, для записей таблицы с данными на листе это — sheetdata row.
Добавим функцию для вывода данных из листа Excel:
printExcelData = function(sheets, strings) {
var unzipProgress = document.getElementById("progress");
unzipProgress.style.display='block';
strings.getData(new zip.TextWriter(), function(text) {
// Получаем все строки листа для ассоциации с их кодами
var i,st=$($.parseXML(decodeURIComponent(escape(text)))).find('si t');
for(i=0;i<st.length;++i)
st[i]=$(st[i]).text();
// Перебираем листы в поисках нужного
var parseSheet=function(sheet){
var j,i,h,sh,d=[],s;
sheet.getData(new zip.TextWriter(), function(text) {
// а вот и наши записи
sh=$($.parseXML(decodeURIComponent(escape(text)))).find('sheetdata row');
// делаем из строки объект
sh.each(function(e){
var c=$(this).find('c'),ci,v,o={};
for(i=0;i<c.length;++i){
ci=$(c[i]);
v=ci.find('v').text();
if(ci.attr('t'))
v=st[v];
j=ci.attr('r').charCodeAt(0)-65;
if(h)
o[h[j]]=v;
else
o[j]=v;
}
if(h){
d.push(o)
} else
h=o;
});
var id_name="";
for(i in h)
if(h[i]=='Comp_Id'){
id_name=h[i];
break;
}
// Если поле Comp_Id есть в записи, значит лист наш
if(id_name=='Comp_Id') {
unzipProgress.style.display='none';
// Это заголовок таблицы данных
s="";
for(i=0;i<Object.keys(h).length;i++)
s+='<th>'+h[i]+'</th>';
$('.result thead tr').append(s)
// Это данные
s="";
for(j=0; j<d.length; j++){
s+='<tr>';
for(i=0; i<Object.keys(h).length; i++){
s+='<td>'+d[j][h[i]].toString()+'</td>';
}
s+='</tr>';
}
$('.result tbody').append(s)
sheets=[];
return;
}
if(sheets.length>0)
parseSheet(sheets.pop());
}, function(current, total) {
unzipProgress.value = current;
unzipProgress.max = total;
});
}
parseSheet(sheets.pop());
}, function(current, total) {
unzipProgress.value = current;
unzipProgress.max = total;
});
}
Автор: AntLogist