Про excel, word...

garrick

Lotus Team
26.10.2009
1 367
152
BIT
364
доработки по граблям, на этот раз поставлено ограничение в процессоре до 10 пустых строк, потому как иначе, на нек. шитах, кол-во строк может оказаться 65тыс. (при реальных 85)
Код:
sheet.getLastRowNum()
Не работает?
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
работает где, в SAX варианте?
там меня процессор не спрашивает, а инициализировать объект еще и для DOM - это опять меморукосампшн
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
факмоймосх... налажал в логике
позиции в файле неправильные (надож глобальные делать)
Java:
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.model.StylesTable;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
 
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.*;
import java.util.*;
 
/**
* Created by mike on 28.07.14.
*/
public class POI2CSV {
private static final String encoding="UTF-8";
private final Map<String,List<Integer>> arrPathCSV = new HashMap<String,List<Integer>>();
private final Map<String,List<Integer>> arrPathMap = new HashMap<String,List<Integer>>();
private final int BLANK_LIM=10;//limit for blank rows, skip other data (stop additions to corresponding List)
private class CSVmap {
private BufferedWriter osCSV;
private BufferedWriter osMAP;
private final File tempcsv;
private final File tempmap;
 
public CSVmap(String xName) throws IOException {
xName=xName.replaceAll("\"","");
tempcsv = File.createTempFile(xName, ".csv");
tempmap = File.createTempFile(xName, ".map");
osCSV= new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempcsv),encoding));
osMAP= new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tempmap), encoding));
}
 
public void close() throws IOException {
osCSV.close();
osMAP.close();
}
 
public void write(String cellReference, String formattedValue) throws IOException {
//			System.out.println(cellReference+SEP);
char SEP = ';';
osMAP.write(cellReference + SEP);
osCSV.write(formattedValue + SEP);
}
 
public void newLine() throws IOException {
osMAP.newLine();
osCSV.newLine();
}
 
public String getTempcsv() {
return tempcsv.getAbsolutePath();
}
 
public String getTempmap() {
return tempmap.getAbsolutePath();
}
 
}
public void parseExcel(File file) throws IOException {
 
OPCPackage container;
try {
container = OPCPackage.open(file.getAbsolutePath());
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(container);
XSSFReader xssfReader = new XSSFReader(container);
StylesTable styles = xssfReader.getStylesTable();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
while (iter.hasNext()) {
InputStream stream = iter.next();
//file.getName()+'_'+iter.getSheetName()
CSVmap csvmap=new CSVmap(file.getName()+'_'+iter.getSheetName());
processSheet(styles, strings, stream, csvmap);
stream.close();
csvmap.close();
}
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (SAXException e) {
e.printStackTrace();
} catch (OpenXML4JException e) {
e.printStackTrace();
}
 
}
 
protected void processSheet(StylesTable styles, ReadOnlySharedStringsTable strings, InputStream sheetInputStream, final CSVmap csvmap) throws IOException, SAXException {
//duplicate for size in newLine
String lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
//length in bytes for default encoding, suppose UTF-8
final int lnsepLen=lineSeparator.getBytes(encoding).length;
final int sepLen=1;
InputSource sheetSource = new InputSource(sheetInputStream);
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
final List <Integer> arrmap=new ArrayList<Integer>();
final List<Integer> arrdata=new ArrayList<Integer>();
arrPathMap.put(csvmap.getTempmap(), arrmap);arrPathCSV.put(csvmap.getTempcsv(), arrdata);
 
try {
SAXParser saxParser = saxFactory.newSAXParser();
XMLReader sheetParser = saxParser.getXMLReader();
ContentHandler handler = new XSSFSheetXMLHandler(styles, strings, new XSSFSheetXMLHandler.SheetContentsHandler() {
int idx =0;//only one index for arrmap, arrdata
int blankcnt=0;//see BLANK_CNT
int lenmap=0, lendata =0;
 
public void startRow(int rowNum) {
if (blankcnt>BLANK_LIM) return;
arrmap.add(0);arrdata.add(0);
idx =arrmap.size()-1;
}
 
public void endRow() {
try {
if (blankcnt>BLANK_LIM) return;
csvmap.newLine();
if (arrmap.get(idx)<=sepLen) {
blankcnt++;}
else{blankcnt=0;} //check only one array (as array sizes are equal)
lenmap+=arrmap.get(idx) + lnsepLen;
arrmap.set(idx, lenmap);
lendata+=arrdata.get(idx) + lnsepLen;
arrdata.set(idx, lendata);
} catch (IOException e) {
e.printStackTrace();
}
}
 
public void cell(String cellReference, String formattedValue) {
if (blankcnt>BLANK_LIM) return;
try {
formattedValue=formattedValue.replaceAll("[\n\r]", "|");
csvmap.write(cellReference, formattedValue);
//set data length for arrays
int len=arrmap.get(idx) + cellReference.getBytes(encoding).length;
arrmap.set(idx,len + sepLen);
len=arrdata.get(idx) + formattedValue.getBytes(encoding).length;
arrdata.set(idx,len + sepLen);
} catch (IOException e) {
e.printStackTrace();
}
}
 
public void headerFooter(String text, boolean isHeader, String tagName) {
 
}
 
},
//new DataFormatter(new Locale("en","UK")),
false//means result instead of formula
);
sheetParser.setContentHandler(handler);
sheetParser.parse(sheetSource);
//			System.out.println("arrmap size:" + arrmap.size()+";->" +csvmap.getTempmap());
} catch (ParserConfigurationException e) {
throw new RuntimeException("SAX parser appears to be broken - " + e.getMessage());}
}
 
public Integer[] getArrMap(String key){
/*		int[] ret= new int[arrmap.size()];
int i = 0;
for (Integer e : arrmap)
ret[i++] = e;
return ret;
 
*/
List<Integer> arr=arrPathMap.get(key);
System.out.println("map index size:" + arr.size());
return arr.toArray(new Integer[0]);
}
 
public Integer[] getArrData(String key){
List <Integer> arr=arrPathCSV.get(key);
System.out.println("csv index size:"+arr.size());
return arr.toArray(new Integer[0]);
}
 
public String[] getArrPathCSV() {
Set<String> keys=arrPathCSV.keySet();
List<String> forsort=new ArrayList<String>(keys);
java.util.Collections.sort(forsort);
return forsort.toArray(new String[0]);
}
 
public String[] getArrPathMap() {
Set<String> keys=arrPathMap.keySet();
List<String> forsort=new ArrayList<String>(keys);
java.util.Collections.sort(forsort);
return forsort.toArray(new String[0]);
}
 
public static void main(String args[]){
POI2CSV inst=new POI2CSV();
try {
System.out.println(args[0]);
inst.parseExcel(new File(args[0]));
String[] mappath=inst.getArrPathMap();
String[] csvpath=inst.getArrPathCSV();
int idx=0;
Integer[] testmap=inst.getArrMap(mappath[idx]);Integer[] test=inst.getArrData(csvpath[idx]);
System.out.println(csvpath[idx]);
System.out.println(mappath[idx]+"; array size:" + mappath.length);
System.out.println(test[3] + "; array size:" + test.length);
System.out.println(testmap[3] + "; array size:" + testmap.length);
RandomAccessFile raf = new RandomAccessFile(mappath[idx], "r");
raf.seek(testmap[3]);
System.out.println("line #5 -->" + raf.readLine());
raf.close();
raf=new RandomAccessFile(csvpath[idx], "r");
raf.seek(test[3]);
FileDescriptor fd = raf.getFD();
FileReader	 fr = new FileReader(fd);
BufferedReader br = new BufferedReader(fr);
System.out.println("csv line #5 -->" +br.readLine());
 
 
} catch (IOException e) {
e.printStackTrace();
}
}
}
кусок
Java:
						lenmap+=arrmap.get(idx) + lnsepLen;
arrmap.set(idx, lenmap);
lendata+=arrdata.get(idx) + lnsepLen;
arrdata.set(idx, lendata);
был без накопления позиции

Добавлено: и вот пара особенностей...
ячейки с переводом каретки - меняю на пайп
даты из хехеля идут в в формате mm/dd/yy - вменяемого способа исправить не нашёл, а лопатить уровнем ниже - нет желания. Потому, затычка уже в ЛС:
Код:
					'если строка и дата содержит разделители /
'предполагаем формат США mm/dd/yy
'при неуказанном формате даты Excel POI SAX выведеn строку в таком формате
If DataType(v)=V_STRING Then
Dim arr:arr=Split(v, DAT_SEP_USA)
If UBound(arr)<>2 Then
Error 1024, CM_WRONGTYPE4DAT _
&{at idx:} &CStr(idx) &{;val:} &CStr(v)
End If
v=DateNumber(arr(2),arr(0),arr(1))
End If

Добавлено: че будет через 100 лет с затычкой :angry2: ?! ведь год краткий (остается надеяться что через 100 лет эхеля не будет)
 
Последнее редактирование модератором:

savl

Lotus Team
28.10.2011
2 625
314
BIT
545
lmike
Спасибо за твои труды, но может уже релиз сделаешь?)
Вот честно, мне не особо хочется в чужой шаблон вставлять чужой же код, а то наманьячу...
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
оттестю и выложу обязательно
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
ну вот и "релиз", в моем понятии - бетта :angry2: (тестил только я, на разных данных)
приделал автоудаление файлов в агенте
в коде много закоменчено, в ходе тестов создавал, есть Print, есть DbgMsg
последний зависи от Debug (глобальная переменная)
набаянил кучу классов, но где-то оставил кондово (типа - возврат массива с двумя значениями - строка;колонка)
в несовсем очевидных местах оставлял каменты, но не усердствовал по комментированию кода :)

Добавлено: проводил тест на 150 листах с данными по 100 строк, сам POI отрабатывает быстро, основное время на нотуса
получается ок 20тыс доков создается и их компутеВизФорм...
можно сократить время создав один док, просчитав его, а далее - копировать все айтемы из документов коллекций

Добавлено: есть особенность (очередная) - хранения процентов будет деленным на 100 (что нормально, но нужно учитывать)

Добавлено: помимо прохода по строкам (тупого) можно указывать привязку поля к конкретной ячейке (добавлено в конфигурацию и ее класс)
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
переделал форму загрузки (выложу позже - там на либы скомпоновать нужно) - позволяет указать подчиненную конфигурацию
importPOI1.png importPOI2.png
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
некоторые обобщения под код :)
вопрос / ответ:
  • для чего эта библиотека (библиотеки/базы) / потребность перенести данные из Excel в нотес и обратно - не являются редкой задачей (как и вобщем - обмен с МСО)
  • почему именно POI / интерфейс к продуктам МСО, в этой библиотеке более-менее отработан и существует в разных вариантах по доступу, можно обеспечить перевод в др. формат (FOP например, а далее в ПДФ) FOP закопали, по остальные варианты здесь
  • почему в текущей реализации (моей библиотеки) использован бридж LS2J / связанно с доступом к памяти и интерграцией в сущ. кодом LS (полагаю он есть у многих). Доступ к памяти специфичен для jvm тем, что требует предварительной настройки (казания кол-ва в ini файле), в тоже время, в LS доступна память до 2Гб. НО В активно развиваемой технологии xPages - несколько др. подход.... Здесь обсуждается только "классический" вариант использования jvm
  • почему используется именно SAX версия / затраты памяти на воссоздание java объектов (в случае с "обычным" вариантом) могут оказаться слишком большими, с чем я и сталкивался (1Гб для jvm не хватило)
  • почему индексные массивы передаются полностью (из jvm в LS), а не используется доступ по индексу / ограничение накладываемое Integer в LS действительно "мешает" обработать файлы, с кол-вом строк > 65К, но весь LS построен на этих ограничениях (поля, индексы, объем хранимой инфы в переменных и т.п.). При это нужно понимать - файлы xlsx большого объема переносить в Домино может оказаться нецелесообразным. В текущем подходе создаются доки 1->1 (строка на док) и создание большого кол-ва документов вызывает некоторую осторожность B). Таки можно реализовать иначе (получать значение по индексу напрямую из jvm) :(
  • чем чревато передача большего кол-ва индексов, чем допустимо в LS массивах / эффектом java object is Null, или выпадением нотусятины в корку (core dump или NSD)
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
к теме относится только как эксель :)
оставлю здесь
ломаем хухель пароль на VBA
типа пошаговое описание моего варианта...
у меня был .xlsb, переименовал (ну это понятно) и распаковал зип
файло находилось по пути xl/vbaProject.bin
открыл с пом. okteta (на бубунте в стандартных репах), на виндядке - читать по ссылке
нашел DPB= и перебил на DPx=
открыл, зашел в VBA (Alt-F11) сохранил/закрыл
не пытаться открывать код без переоткрытия!
Это метод "противодействия" говнокодерам, кот. паролем защищают свои поделия (лучи поноса им) :)
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
пусть полежит здесь
РТФ и хтмл (RTF, HTML)
 

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
для ворда ссылка про Aspose
все это же можно решать и на POI, НО не будет "полноценной" выгрузки в PDF
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
так или иначе затрагивает тему...
вот тут " " (с) обыкновенное чудо
как по ссылке (иносказательно ;) ) - ХЦЛ не предлагает, но забивает на клиентскую java

отсюда 2-а вывода (для java кода):
-перенос приложений на сервер (что не всегда удобно/целесообразно)
-перенос на аппсервер с поддержкой java
у меня лично тлела надежда - что ИБМ и иже с ними одумаются и продолжат интеграцию java, но нет - они откопали стюардессу LS и какбэ "успокоились"
да, с 10-ой версии (а точнее - раньше ;) ) появилист классы для работы с http, это снимает часть проблем, но только часть. SSL, proxy в этих классах под вопросом, а то как они прелагают работать с json у меня (и не только) вызывает содрогание :)

план действий:
-я остановил свой выбор на tomcat - "простой", быстрый, открытый (этот факт меня "согревает" возможностью поддержки сообществом, если продукт будет "нужен"), есть деплоймент из популярных ИДЕ
-связь с доминой/нотуснёй через обмен json
-обмен файлами через ФС (да - предполагвется использовать его локально)

почему так? ЖВМ в домине живет в 3-х ипостасях/реинкарнациях, причем одновременно и все они подчиняются разным правилам и время жизни объектов разное
-агенты - завершение агента совершенно не означает сборку объектов за ним, она не моментальная, а в нек. случаях не произойдёт. Класслоадер свой и поддержка синглтонов ток из jvm/lib/ext
-адыны - серверная часть, разработка/отладка сопряжена со сложностями, их судьба для меня не ясна (для ХЦЛ, я полагаю, тоже ;) )
-xpages (JSF, OSGi и прочая) живет в рамках http задачи, НО сама http можешь нещадно течь и ронять весь сервер (совсем не веселая перспектива). ХЦЛ её не собирается убивать, но и обновление стэка там, мягко-говоря, сильно замшело (и врядли это исправится)

Добавим увлечение ХЦЛ модной/молодёжной нодой, которая пока пришпандоривается к домине подпорками сбоку
 
Последнее редактирование:
  • Нравится
Реакции: alexas1

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
выложил на ГХ сорцы для использования с (для одной локации, типа сотрудники обращаютсяк серверам ток из )
описание в процессе
ODP не думаю что скоро (много выдергивать надо или переделывать на упрощенную логику) и скорее-всего в связанном проекте
могет:
- получать списки полей и именованных таблиц из DOC/DOCX, таблицы может выгружать как отдельные файлы
- заменять текст по шаблону, щас использутется <*>
- заменять цельные таблицы (сопоставляя по title) из списка файлов (json с именем и путем)
- сохранения в любой формат, поддерживаемый Aspose
всё это работает и без всякой нотусни, на томкате

в нотусне:
- парсинг json, спасибо @VladSh (немного поменял код под себя)
- интеграция с томкатом
1603454642177.png

- получение полей из документов-полей (навигатор с всякими дополнениями). Т.е. каждое поле - отдельный док, фильтруются по общему документу и некоему префиксу (в имени поля - поле в документе-поле). Получение List и конвертация его в жсон, подстановка по референсам (значение поля д.б. из другого поля)
- там много ещё чего (долго пилилась база): шаблоны документов, версии полей (хочу переписать), подгрузка шаблонов из DOCX... Но это на старом движке (КОМа) от него и есть план уйти (из-за ограничений ворда и скорости работы)
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
отступление про excel, но в срезе aspose
там есть ф-ция высокоуровневая (мне это не нра) она сразу из диапазона делает жсон +/-:
+++++
- это удобно
- есть минимальные настройки - по использованию заголовка (колонок) и форматирования
- на больших объемах не тестировал - работает быстро
-----
- сохраняет в стринг, без вариантов (память)
- чтобы разделить нужно что-то делать, т.е. накладные расходы на код и память (например javax.json)

остальное - как и у всех продуктов аспоз - выходные форматы достаточно богаты
уровень абстракции выше чем в POI - это удобно

эконом-вариант - POI, но там много "но и если", хотя вот в данном случае (aspose.cells) - нет большого стимула его использования (только в составе общего набора)

По моему опыту, набор который интересен в применении:
- aspose.words - регулярки, метасимволы (например параграф- &p), свои ф-ции для поиска и замены... удобно
- aspose.barcode - куча типов, распознаёт уверенно, некритично в положению листа в скане, аналог (OSS) - zxing (все характеристики хуже)
- aspose.pdf - деление на страницы, соединение, преобразование в разные форматы (и что важно - в графике можно задавать разрешение), извлечение различных объектов (текст, графика...), работа с областями по координатам (это типично для ПДФ), полных и качественных аналогов нет, близко - pdfbox (если не быть притязательным)
остальные не использовал - увы, не могу поделиться впечатлениями
для полноценного использования jvm, настоятельно рекомендую - не использовать таковую от нотус/домино как это обойти уже рассказывал
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
очередной подход к jxlsx и вин ;)
взял либу выгрузки в жсон, с openntf
поморочился с шаблоном когда группировка
суть - низя использовать элемент List как группировку по индексу
вот так будет работать

1694459057230.png

а ежели в groupBy использовать ${get(9)} (что по сути тоже значение в классе) - не будет группировки
т.е поле д.б с геттером!
обходил так (поле в жсон, по фильтру последнее)
1694459348645.png

т.к. из жсон считываются items в List построчно (List table) - надо транспонировать, поэтому и транспонирование: поле "по порядку" - колонка, мультивэлью, строки,
в jackson, считываем так

Java:
package org.lmike;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@JsonIgnoreProperties(ignoreUnknown = true)
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class NotesDocument {
    @JsonProperty("UniversalID")
    String unid;
    @JsonProperty("Items")
    JsonNode items;
}
Java:
package org.lmike;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class MyColumns <T> {
    List<T> lst=new ArrayList<>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    String name;
    public T get(int index) {
        return lst.get(index);
    }

    public int size() {
        return lst.size();
    }

    public void add(T t) {
        lst.add(t);
    }
}
Java:
    public List<NotesDocument> unmarshal(InputStream in) throws IOException {
        ObjectMapper mapper = JsonMapper.builder()
                .addModule(new JavaTimeModule())
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .build();
        //https://stackoverflow.com/a/6349488

        return mapper.readValue(in,
                mapper.getTypeFactory().constructCollectionType(List.class, NotesDocument.class));
    }
    public static void main(String[] args) throws IOException {
    ....
    List<NotesDocument> notesDocuments=obj.unmarshal(in);
    ....
                    Iterator<Map.Entry<String, JsonNode>> iterator=notesDocuments.get(0).items.fields();
                    List<List<String>> table = new ArrayList<>();
                    while (iterator.hasNext()) {
                        Map.Entry<String, JsonNode> entry = iterator.next();
                        if (entry.getKey().toLowerCase().startsWith("name_")) {
                            LOG.debug(entry.getKey() + ":" + entry.getValue());
                            List<String> values=StreamSupport
                                    .stream(entry.getValue().get("Values").spliterator(), false)
                                    .map(JsonNode::asText)
                                    .collect(Collectors.toList());
                            table.add(values);
                        }
                    }
                    List<MyColumns<String>> tableValues=myTranspose(table);
                    context.putVar("tableValues", tableValues);
 
Последнее редактирование:

lmike

нет, пердело совершенство
Lotus Team
27.08.2008
7 985
611
BIT
473
кстати - обращение к индексу группы - ах@енная фича
результат по группам
1694460217512.png

по шаблону выше
1694886827036.png

1694886868775.png

и это штатная фича движка (как и переменная _group), на уровне кода не нужно ничего делать
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!