fix(dav): upload --replace не заменяет при серверной конвертации .xls→.xlsx (дубликаты) #82

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

Проблема

oo dav upload FOLDER LOCAL.xls --replace заливает локальный .xls, но OnlyOffice
конвертирует его в .xlsx при загрузке. UploadToFolderReplacing дедуплицирует по
stem|ext (.xls) через DeleteFilesByDedupKey и не находит сохранённый .xlsx,
поэтому повторный upload создаёт второй файл.

Проверено live на My Documents/Banks/Caixa/extractos/:
id 3799 + 3887 (ES29…) и id 3800 + 3888 (ES87…) — по два одинаковых .xlsx на каждый
повторный caixa publish.

Что сделать

  • --replace должен матчить существующие файлы по stem с учётом серверной
    конвертации: legacy ext (.xls/.doc/.ppt) ↔ OOXML (.xlsx/.docx/.pptx); либо для
    документов дедуплицировать по stem.
  • Предпочтительно при совпадении по stem использовать существующий id
    (UpdateFile) вместо delete+upload — id стабилен, нет окна без файла.
  • Покрыть тестом (fake/интеграционный): повторный upload одного и того же .xls не плодит дубликаты.

Acceptance

  • повтор oo dav upload <folder> f.xls --replace → в папке один файл на stem (без дублей);
  • oo dav ls подтверждает один файл; поведение прочих форматов не сломано;
  • go test -race ./..., go vet ./..., gofmt -l . — чисто.

Оценка: S

## Проблема `oo dav upload FOLDER LOCAL.xls --replace` заливает локальный `.xls`, но OnlyOffice **конвертирует его в `.xlsx`** при загрузке. `UploadToFolderReplacing` дедуплицирует по `stem|ext` (`.xls`) через `DeleteFilesByDedupKey` и **не находит** сохранённый `.xlsx`, поэтому повторный upload создаёт **второй файл**. Проверено live на `My Documents/Banks/Caixa/extractos/`: `id 3799 + 3887` (ES29…) и `id 3800 + 3888` (ES87…) — по два одинаковых `.xlsx` на каждый повторный `caixa publish`. ## Что сделать - `--replace` должен матчить существующие файлы по **stem** с учётом серверной конвертации: legacy ext (`.xls/.doc/.ppt`) ↔ OOXML (`.xlsx/.docx/.pptx`); либо для документов дедуплицировать по stem. - Предпочтительно при совпадении по stem использовать существующий id (`UpdateFile`) вместо delete+upload — id стабилен, нет окна без файла. - Покрыть тестом (fake/интеграционный): повторный upload одного и того же `.xls` не плодит дубликаты. ## Acceptance - [ ] повтор `oo dav upload <folder> f.xls --replace` → в папке **один** файл на stem (без дублей); - [ ] `oo dav ls` подтверждает один файл; поведение прочих форматов не сломано; - [ ] `go test -race ./...`, `go vet ./...`, `gofmt -l .` — чисто. Оценка: S
Author
Owner

SE: взял в работу. Ветка fix/dav-replace-convert#82 от main (worktree). Причина: UploadToFolderReplacing матчит по stem|ext через FindFilesByDedupKey, а OO конвертирует .xls→.xlsx при upload — совпадения нет. План TDD: (1) offline unit-тесты на conversion-aware матчинг (.xls↔.xlsx) и план замены; (2) при совпадении по stem — UpdateFile (id стабилен) с fallback delete+upload; (3) без регрессий для pdf/xlsx. Live-проверка: в process env нет ONLYOFFICE_* — сделаю offline; если поднимутся креды, прогоню на временном проекте.

SE: взял в работу. Ветка `fix/dav-replace-convert#82` от `main` (worktree). Причина: `UploadToFolderReplacing` матчит по `stem|ext` через `FindFilesByDedupKey`, а OO конвертирует `.xls`→`.xlsx` при upload — совпадения нет. План TDD: (1) offline unit-тесты на conversion-aware матчинг (`.xls`↔`.xlsx`) и план замены; (2) при совпадении по stem — `UpdateFile` (id стабилен) с fallback delete+upload; (3) без регрессий для pdf/xlsx. Live-проверка: в process env нет `ONLYOFFICE_*` — сделаю offline; если поднимутся креды, прогоню на временном проекте.
Author
Owner

Отчёт SE

Ветка: fix/dav-replace-convert#82 (от main, 74e72d0) → PR #83 #83

Причина

OO конвертирует legacy-бинарные форматы при upload (.xls/.doc/.ppt → .xlsx/.docx/.pptx). UploadToFolderReplacing матчил точно по stem|ext (FindFilesByDedupKey), поэтому повторный oo dav upload FOLDER f.xls --replace не находил сохранённый f.xlsx → каждый повтор создавал второй файл (live: 3799+3887, 3800+3888).

Стратегия дедупа

  • EquivalentUploadExt / FindFilesByStemExt — матч по stem + equivalence расширений: legacy .xls/.doc/.ppt ↔ OOXML .xlsx/.docx/.pptx; для прочих форматов поведение прежнее (pdf, xlsx совпадают точно).
  • planUploadReplacement — по листингу папки выбирает выживающий файл (самый свежий) и лишние дубликаты.
  • UploadToFolderReplacing:
    • нет совпадения → обычный upload (как раньше);
    • есть совпадение → UpdateFile по существующему id (id стабилен, нет окна удаления) + удаление лишних дублей;
    • UpdateFile не поддержан порталом → fallback на conversion-aware DeleteFilesByStemExt + upload (один файл на stem в любом случае).
  • AssertNoFileConflict (--no-replace) тоже conversion-aware: .xls теперь конфликтует с существующим .xlsx.
  • Добавлен DeleteFilesByStemExt; старый DeleteFilesByDedupKey не тронут (используется replace-in).

Тесты

  • files_replace_test.go (offline, pure): EquivalentUploadExt (симметрия, кейсы), FindFilesByStemExt (конверсия + точный матч, pdf/xlsx не сломаны), planUploadReplacement (fresh/update/collapse), регрессия TestPlanUploadReplacementRepeatedXLS — повторный .xls даёт update существующего id, не создание второго.
  • Проверено: go vet ./..., go test -race -shuffle=on -count=1 ./..., gofmt -l, go mod tidy (без диффа), сборка cmd/oo и cmd/office — всё чисто.

Live

В process env нет ONLYOFFICE_* (в .env не заглядывал), поэтому live-прогон не выполнялся — только offline-тесты. CI-workflow репо — GitHub Actions, на Gitea прогонов нет (po run пусто); эквивалентные команды прогнал локально.

Открытый вопрос / риск

UpdateFile при кросформатном (конвертируемом) обновлении: нужно live-подтвердить, что PUT /files/{id}/update с payload .xls конвертирует его в .xlsx в рамках существующего id. Error-fallback покрыт, но молчаливая порча формата — нет. Плюс: UpdateFile копит версии (см. докстринг replace-in про «clean version / shared file id») — для дефолтного upsert это осознанный размен по формулировке issue («предпочтительно UpdateFile, id стабилен»). Прошу подтвердить приоритет при live-проверке.

Не мержу — жду ревью PO.

## Отчёт SE **Ветка:** `fix/dav-replace-convert#82` (от `main`, 74e72d0) → **PR #83** https://git.produktor.io/eSlider/go-onlyoffice/pulls/83 ### Причина OO конвертирует legacy-бинарные форматы при upload (`.xls/.doc/.ppt` → `.xlsx/.docx/.pptx`). `UploadToFolderReplacing` матчил точно по `stem|ext` (`FindFilesByDedupKey`), поэтому повторный `oo dav upload FOLDER f.xls --replace` не находил сохранённый `f.xlsx` → каждый повтор создавал второй файл (live: 3799+3887, 3800+3888). ### Стратегия дедупа - `EquivalentUploadExt` / `FindFilesByStemExt` — матч по stem + equivalence расширений: legacy `.xls/.doc/.ppt` ↔ OOXML `.xlsx/.docx/.pptx`; для прочих форматов поведение прежнее (`pdf`, `xlsx` совпадают точно). - `planUploadReplacement` — по листингу папки выбирает выживающий файл (самый свежий) и лишние дубликаты. - `UploadToFolderReplacing`: - **нет совпадения** → обычный upload (как раньше); - **есть совпадение** → `UpdateFile` по существующему id (id стабилен, нет окна удаления) + удаление лишних дублей; - `UpdateFile` не поддержан порталом → fallback на conversion-aware `DeleteFilesByStemExt` + upload (один файл на stem в любом случае). - `AssertNoFileConflict` (`--no-replace`) тоже conversion-aware: `.xls` теперь конфликтует с существующим `.xlsx`. - Добавлен `DeleteFilesByStemExt`; старый `DeleteFilesByDedupKey` не тронут (используется `replace-in`). ### Тесты - `files_replace_test.go` (offline, pure): `EquivalentUploadExt` (симметрия, кейсы), `FindFilesByStemExt` (конверсия + точный матч, pdf/xlsx не сломаны), `planUploadReplacement` (fresh/update/collapse), регрессия `TestPlanUploadReplacementRepeatedXLS` — повторный `.xls` даёт update существующего id, не создание второго. - Проверено: `go vet ./...`, `go test -race -shuffle=on -count=1 ./...`, `gofmt -l`, `go mod tidy` (без диффа), сборка `cmd/oo` и `cmd/office` — всё чисто. ### Live В process env нет `ONLYOFFICE_*` (в `.env` не заглядывал), поэтому live-прогон **не выполнялся** — только offline-тесты. CI-workflow репо — GitHub Actions, на Gitea прогонов нет (`po run` пусто); эквивалентные команды прогнал локально. ### Открытый вопрос / риск `UpdateFile` при кросформатном (конвертируемом) обновлении: нужно live-подтвердить, что `PUT /files/{id}/update` с payload `.xls` конвертирует его в `.xlsx` в рамках существующего id. Error-fallback покрыт, но молчаливая порча формата — нет. Плюс: `UpdateFile` копит версии (см. докстринг `replace-in` про «clean version / shared file id») — для дефолтного upsert это осознанный размен по формулировке issue («предпочтительно UpdateFile, id стабилен»). Прошу подтвердить приоритет при live-проверке. Не мержу — жду ревью PO.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eSlider/go-onlyoffice#82