fix(dav): UpdateFile ломает конвертируемый ext (.xls→.xlsx) — сырой OLE2 под .xlsx #84

Closed
opened 2026-09-27 15:09:38 +01:00 by eSlider · 2 comments
Owner

Проблема (регрессия от #82)

UploadToFolderReplacing теперь матчит конвертируемые расширения (.xls↔.xlsx) и при
совпадении вызывает UpdateFile. Но OnlyOffice не конвертирует тело при UpdateFile,
поэтому сырой .xls (OLE2) ложится под прежним именем .xlsx — расширение не
соответствует содержимому, файл не открывается.

Живое доказательство (2026-09-27, Banks/Caixa/extractos/):

  • после UpdateFile id 3799/3800 имели размер 192000/7168 B и magic d0cf 11e0 (OLE2 .xls)
    при title .xlsx;
  • после rm + свежего upload сервер сконвертировал в настоящий .xlsx (50743 B, PK).

Что сделать

  • В UploadToFolderReplacing применять UpdateFile только когда расширения совпадают
    (stored ext == local ext).
  • Если матч по stem сработал через эквивалентность (legacy .xls/.doc/.ppt ↔ OOXML),
    делать delete (все дубли по stem) → upload — пусть сервер конвертирует в OOXML.
  • AssertNoFileConflict (--no-replace) оставить conversion-aware, но без UpdateFile-пути.
  • Тест: повторный upload .xls при существующем .xlsx → файл перезалит (несколько id меняется),
    содержимое — валидный OOXML (не OLE2 под .xlsx).

Acceptance

  • повторный oo dav upload <folder> f.xls --replace → один файл, содержимое валидный .xlsx;
  • exact-ext (pdf→pdf, xlsx→xlsx) по-прежнему через UpdateFile (регрессия покрыта);
  • offline-тесты; go test -race ./..., go vet ./..., gofmt -l . — чисто.

Оценка: S

## Проблема (регрессия от #82) `UploadToFolderReplacing` теперь матчит конвертируемые расширения (`.xls`↔`.xlsx`) и при совпадении вызывает `UpdateFile`. Но OnlyOffice **не конвертирует** тело при `UpdateFile`, поэтому сырой `.xls` (OLE2) ложится **под прежним именем `.xlsx`** — расширение не соответствует содержимому, файл не открывается. Живое доказательство (2026-09-27, `Banks/Caixa/extractos/`): - после `UpdateFile` id 3799/3800 имели размер 192000/7168 B и magic `d0cf 11e0` (OLE2 `.xls`) при title `.xlsx`; - после `rm` + свежего upload сервер сконвертировал в настоящий `.xlsx` (50743 B, `PK`). ## Что сделать - В `UploadToFolderReplacing` применять `UpdateFile` **только когда расширения совпадают** (`stored ext == local ext`). - Если матч по stem сработал через **эквивалентность** (legacy `.xls/.doc/.ppt` ↔ OOXML), делать **delete (все дубли по stem) → upload** — пусть сервер конвертирует в OOXML. - `AssertNoFileConflict` (--no-replace) оставить conversion-aware, но без UpdateFile-пути. - Тест: повторный upload `.xls` при существующем `.xlsx` → файл перезалит (несколько id меняется), содержимое — валидный OOXML (не OLE2 под `.xlsx`). ## Acceptance - [ ] повторный `oo dav upload <folder> f.xls --replace` → один файл, содержимое валидный `.xlsx`; - [ ] exact-ext (pdf→pdf, xlsx→xlsx) по-прежнему через UpdateFile (регрессия покрыта); - [ ] offline-тесты; `go test -race ./...`, `go vet ./...`, `gofmt -l .` — чисто. Оценка: S
Author
Owner

Начинаю. План: TDD — сначала тесты на planUploadReplacement (exact-ext → UpdateFile; legacy↔OOXML → delete+upload), затем правка files_stem.go. AssertNoFileConflict уже conversion-aware и UpdateFile не вызывает — не трогаем. Live — по наличию env.

Начинаю. План: TDD — сначала тесты на planUploadReplacement (exact-ext → UpdateFile; legacy↔OOXML → delete+upload), затем правка files_stem.go. AssertNoFileConflict уже conversion-aware и UpdateFile не вызывает — не трогаем. Live — по наличию env.
Author
Owner

Готово — PR #85 (#85), ветка fix/dav-updatefile-convert#84, коммит aad6315.

Причина

UploadToFolderReplacing матчил legacy↔OOXML (.xls ↔ сохранённый .xlsx) через EquivalentUploadExt и вызывал UpdateFile. OnlyOffice при update тело не конвертирует → сырой .xls (OLE2) оставался под именем .xlsx.

Правило update vs delete+upload

  • planUploadReplacement: UpdateFile ставится только если FileEntryExt(keep) == normalizeExt(ext) (расширения точно совпадают, включая пустое).
  • Матч по эквивалентности (расширения разные) → keeper + все дубли идут в DeleteIDs, UpdateID пуст.
  • UploadToFolderReplacing: при UpdateID != "" — update in-place (+ удаление лишних дублей); иначе — delete всех совпадений и свежий upload (сервер конвертирует). Fallback при ошибке update (delete+upload) сохранён.
  • AssertNoFileConflict (--no-replace) не менялся: он уже conversion-aware и UpdateFile не вызывает.

Тесты

  • Правленые/новые offline-тесты в files_replace_test.go: exact-ext (pdf→pdf, xlsx→xlsx, xls→xls) → UpdateID, без delete; converted .xls при .xlsx → delete+upload; коллапс converted-дублей; повторный .xls → delete+upload (регрессия #84).
  • go test -race ./... — ok (все пакеты), go vet ./... — чисто. gofmt -l по моим файлам пусто; в репо есть пре-существующие (не мои) неотформатированные cmd/office/... файлы — не трогал (вне scope).

Live (реальный OO, временная папка oo-it-84-*, удалена)

  • 1-й upload test.xls (публичный OLE2-фикстур) → kit84.xlsx, 8192 B, download Microsoft Excel 2007+ (PK).
  • Повтор upload .xls → таблица replaced=3889 (delete+upload, не UpdateFile), в папке один файл, download снова валидный OOXML PK.
  • pdf→pdf повтор → тот же id 3890, replaced пусто (путь UpdateFile) — регрессии нет.
  • Папка удалена через oo dav rm --folders. Banks/Caixa не трогал.
## Готово — PR #85 (https://git.produktor.io/eSlider/go-onlyoffice/pulls/85), ветка `fix/dav-updatefile-convert#84`, коммит aad6315. ### Причина `UploadToFolderReplacing` матчил legacy↔OOXML (`.xls` ↔ сохранённый `.xlsx`) через `EquivalentUploadExt` и вызывал `UpdateFile`. OnlyOffice при update тело не конвертирует → сырой `.xls` (OLE2) оставался под именем `.xlsx`. ### Правило update vs delete+upload - `planUploadReplacement`: `UpdateFile` ставится **только если `FileEntryExt(keep) == normalizeExt(ext)`** (расширения точно совпадают, включая пустое). - Матч по эквивалентности (расширения разные) → keeper + все дубли идут в `DeleteIDs`, `UpdateID` пуст. - `UploadToFolderReplacing`: при `UpdateID != ""` — update in-place (+ удаление лишних дублей); иначе — delete всех совпадений и свежий upload (сервер конвертирует). Fallback при ошибке update (delete+upload) сохранён. - `AssertNoFileConflict` (`--no-replace`) не менялся: он уже conversion-aware и `UpdateFile` не вызывает. ### Тесты - Правленые/новые offline-тесты в `files_replace_test.go`: exact-ext (pdf→pdf, xlsx→xlsx, xls→xls) → `UpdateID`, без delete; converted `.xls` при `.xlsx` → delete+upload; коллапс converted-дублей; повторный `.xls` → delete+upload (регрессия #84). - `go test -race ./...` — ok (все пакеты), `go vet ./...` — чисто. `gofmt -l` по моим файлам пусто; в репо есть **пре-существующие** (не мои) неотформатированные `cmd/office/...` файлы — не трогал (вне scope). ### Live (реальный OO, временная папка `oo-it-84-*`, удалена) - 1-й upload `test.xls` (публичный OLE2-фикстур) → `kit84.xlsx`, 8192 B, download `Microsoft Excel 2007+` (`PK`). - Повтор upload `.xls` → таблица `replaced=3889` (delete+upload, не UpdateFile), в папке один файл, download снова валидный OOXML `PK`. - `pdf→pdf` повтор → тот же id 3890, `replaced` пусто (путь UpdateFile) — регрессии нет. - Папка удалена через `oo dav rm --folders`. `Banks/Caixa` не трогал.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eSlider/go-onlyoffice#84