Rychlejší způsob načítání souborů ze SharePointu v Power BI
- Vojtěch Šíma
- Dec 30, 2025
- 7 min read
tl;dr Pokud chceš dosáhnout nejlepšího výkonu pro velké množství souborů, použij SharePoint.Contents s ApiVersion = 15 a dej všechny potřebné soubory do jedné složky.
Rozdíl mezi SharePoint.Files a SharePoint.Contents
SharePoint.Files a SharePoint.Contents jsou dvě vestavěné funkce v Power Query, které slouží k snadnému získání dat ze SharePointu pomocí uživatelsky přívětivého klikání. Tedy alespoň ta první. Pokud se proklikáš nabídkou a vybereš "SharePoint folder" ze seznamu konektorů, automaticky se použije funkce SharePoint.Files.
Možná jsi už slyšel o problémech nebo potenciální pomalosti téhle automatické funkce, takže jsi ručně přepsal "Files" na "Contents" a teď používáš SharePoint.Contents.
Otázka je ale, jestli jsi tím něco vyřešil.
Jednoduché vysvětlení, proč je jeden nebo druhý lepší/rychlejší/silnější, je následující. V říši klikání to funguje tak, že když použiješ Files, vylistuješ veškerý binární obsah z celé SharePoint site a pak si vybereš to, co chceš, pomocí filtrování nebo přímého výběru. V případě Contents máš k dispozici jednoduchou navigaci, která prochází složky; podobně jako v jakémkoli správci souborů. Takže si proste klikáš složky a podsložky.
Proč je to pomalé a kdo je rychlejší?
Obecně by měl být Contents rychlejší, ALE záleží na několika faktorech. Když nebudu mluvit příliš technicky (později to vysvětlím do velkého detailu, neboj), rychlost bude svázána s celkovým počtem souborů na tvém SharePointu a přesnou verzí Contents/Files, kterou použiješ. V tuto chvíli, pokud nezadáš žádnou verzi, Contents poběží na verzi 14. Tato verze se chová podobně jako výchozí chování Files: načte metadata a provede discovery, poté (v případě Contents) naviguje složku po složce nebo podle navigace, kterou sis definoval, načte kontextové informace o souboru a nakonec stáhne obsah souboru.
Tohle je docela neefektivní a Contents s defaultní API verzí může mít dokonce horší výkon než stejný test s Files.
S ApiVersion = 15 dává Contents najednou smysl. Přeskakuje většinu kontrol metadat a discovery fází a načítá obsah po složkách okamžitě, přičemž kopíruje tvou navigaci. A to se vyplatí.
Je to ale rychlejší než Files? No, tady přichází ta část "záleží": čím více souborů na webu máš, tím rychlejší Contents bude, ale ne drasticky. I když Files posílá extra web requesty, rychlost není o tolik horší, pokud se snažíš napodobit stejné chování jako u Contents. Rozdíl by měl být svázán s discovery fází u Files, která je stránkována po tisících, takže jakmile překročíš 1001 souborů, měl bys začít být o něco rychlejší. Také technicky děláš extra request pro každý jednotlivý soubor, takže tyto milisekundy se nasčítají.
Abych to shrnul, použití ApiVersion = 15 a SharePoint.Contents by mělo být rychlejší.
= SharePoint.Contents("https://tenant.sharepoint.com/sites/siteName", [ApiVersion=15])Existuje také možnost zvolit Implementation, ale při použití ApiVersion=15 a Implementation="2.0" oproti nepoužití této implementace jsem nenašel velký rozdíl.
Výsledky Benchmarku
Společné nastavení testu
Načtené soubory: 79 (Identické .xlsx soubory)
Velikost souboru: 1.25 MB na soubor
Struktura dat: 4 sloupce, 58 605 řádků na soubor
Celkový objem: 4 629 716 řádků
Přesnost: přibližně ±5 sekund.
Scénář č. 1
Kontext: 7 325 souborů celkem na stránce/site.
Konektor | Možnosti funkce | Čas (mm:ss) |
SharePoint.Contents | ApiVersion = 14 (Default) | 02:00 |
SharePoint.Files | ApiVersion = 15 (Default) | 01:00 |
SharePoint.Contents | ApiVersion = 15 | 00:53 |
SharePoint.Contents | ApiVersion = 15, Implementation = 2.0 | 00:52 |
Scénář č. 1
Kontext: 26 573 souborů celkem na stránce/site
Nastavení je identické jako u Scénáře č. 1, ale s výrazně větším počtem souborů umístěných mimo cílovou složku, avšak stále na stejném webu/stránce.
Konektor | Možnosti funkce | Čas (mm:ss) |
SharePoint.Contents | ApiVersion = 14 (Default) | 05:03 |
SharePoint.Files | ApiVersion = 15 (Default) | 01:30 |
SharePoint.Contents | ApiVersion = 15 | 00:50 |
SharePoint.Contents | ApiVersion = 15, Implementation = 2.0 | 00:55 |
Dlouhá doba načítání při použití ApiVersion = 14 u SharePoint.Contents je primárně způsobena starými OData requesty provádějícími discovery; tyto requesty tak trochu samy sobě brání v rychlejším odesílání.
Z benchmarků můžeš vidět, že s nárůstem počtu souborů musí konektor SharePoint.Files provádět dodatečné discovery, což ho zpomaluje. Naproti tomu SharePoint.Contents (s ApiVersion = 15) je stabilní a čísla jsou identická (v rámci uvedené přesnosti). Proto by při správném použití měl být vítězem SharePoint.Contents.
Detailnější popis korelace s mnžostvím souborů níže
Dodatečná poznámka ke kombinování souborů
Ještě jedna poznámka navíc: ať už použiješ snadnou možnost kombinování souborů klikáním (ta ošklivá vestavěná věc, co vytvoří hromadu extra balastu), nebo si definuješ transformaci pro každý soubor a aplikuješ ji sám, z hlediska rychlosti nebo zpracování se nic moc nemění. Ale samozřejmě, postavit si to sám je čistší a snáze spravovatelné... a taky je za to zlatý bludišťák.
Alternativní rychlý způsob načítání SharePoint souborů
Teď se pojďme podívat, jestli existuje ještě rychlejší způsob. Spoiler: jo, ale jen o trošičku. Možná stáhneš jednu nebo dvě sekundy.
SharePoint sám o sobě není úplně stavěný na hromadné stahování souborů (bulk retrieval) a není to ani úplně to správné úložiště. Microsoft rozhodně nabízí lepší řešení, ale pro tento článek je budu ignorovat. Mohl bych jednoduše říct, že nejrychlejší způsob, jak načíst soubory ze SharePointu, je přesunout je jinam, parsovat je do databáze a data tahat odtamtud, ale to jde tak trochu proti smyslu celého tohoto článku.
okud je tu nějaký čtenář, který rád říká "ehm, ackchyually,", jasně, máš pravdu: buď implementuj inkrementální refresh na SharePoint zdroji, nebo přesuň soubory pryč. Rozhodně existuje lepší způsob než denně ládovat desítky nebo stovky souborů do Power BI.
Když máme tohle z krku, pojďme se podívat na to, jak dostat soubory přímo ze SharePointu co alternativním rychlým způsobem, uvnitř Power Query.
SharePoint.Files vs SharePoint.Contents z technické hlediska
Dříve jsem ti slíbil technické detaily o vnitřním fungování SharePoint.Files a SharePoint.Contents. Abychom pochopili a lépe uchopili nejrychlejší způsob sběru souborů, dovol mi nejdříve představit klíčové koncepty, které potřebujeme k vybudování lepšího řešení.
Pro toto použiji ApiVersion = 15 u obou, aby byly testy fér.
SharePoint.Files
Proces začíná voláním pro získání Document library. To se děje přes sites/Experiments/_api/web/lists/? s filtry pro získání nesystémových dokumentů s více než 'žádným' souborem.
Poté pošle requesty pro metadata a s ID document library začne discovery fázi skrze /RenderListDataAsStream jako POST request. Toto je drahý způsob, jak získávat položky na SharePointu. Je drahý, protože vrací spoustu dat navíc; je to tak trochu myšleno pro vizuální reprezentaci na webu, takže pro joby na pozadí je to příliš heavy.
Sskenuje všechny složky a podsložky a hledá soubory. Tento sken je extrémně divný; defaultně skenuje celou site po 1 000 položkách na stránku. Nicméně, pokud jsou složka a soubory, které chceš, na začátku seznamu, může to skenování zastavit, jakmile je najde. Ale to "může" je ta divná část.
Tady je příklad: Řekněme, že tvá existující složka má ID 7489. Všechny soubory pod ní jsou posloupné, mají inkrement plus jedna, třeba od ID 7490 do 7800. Všechny soubory a jakákoli podsložka v té cestě se vejdou do té konkrétní stránky, takže pak proces "může" přestat skenovat. Avšak pokud nyní přidáš nový soubor do té složky s ID 13000, protože jsi mezitím vytvořil 5 000 souborů jinde, můžeš tento hladký průběh skenování rozbít. Engine nyní musí stránkovat dál skrz všechny soubory na webu/site, aby našel ten poslední soubor. To je teorie, kterou jsem zatím posbíral.
Stále nejsem s tímto chováním či vysvětlením na stopro happy, takže očekávej v budoucnu třeba nějaké upřesnění. Ale v podstatě pro teď si jen pamatuj: každý soubor a složka má unikátní ID, které je vytvářeno sekvenčně. Pokud přidáš nové soubory do starších složek, budeš muset proskenovat celý web, protože se obsah načítá chronologicky, čímž se zvyšuje čas načítání dat.
Jakmile data najdeš, pro každý soubor to pošle api/contextinfo. Tento request je vyžadován kvůli digest renderu z discovery fáze a obsahuje metadata, nicméně je to pár bytů velké, takže žádná hrůza. No a potom všem stáhne obsah souboru pomocí api/web/getfilebyserverrelativeurl(path)/$value, což je vlastní soubor.
SharePoint.Contents
Tohle je mnohem přímočařejší a tvoje hlava u toho nebude dělat 'jauvajs jauvajs'. Proces začíná replikací navigace, kterou sis nakonfiguroval ve svých krocích v Power Query. Podle tvé nagiace tedy pošleš api/Web/GetFolderByServerRelativePath(path)/Folders a api/Web/GetFolderByServerRelativePath(path)/Files z kořenové složky knihovny až do poslední složky, kterou jsi definoval ve svých krocích.
Jakmile je tohle hotové, podobně jako u SharePoint.Files, začne to stahovat samotné soubory pomocí _api/web/getfilebyserverrelativeurl(path)/$value, a to je v podstatě vše.
Vlastní řešení pro získání dat ze SharePointu
Teď, když chápeme, co SharePoint dělá pod pokličkou, můžeme tento proces replikovat, a trochu ho optimalizovat. Zkoumal jsem další potenciální endpointy, abych zjistil, jestli bychom nemohli získat data hromadně efektivněji, ale pro use casy v Power Query jsou možnosti omezené. Dokonce ani Microsoft Graph API nenabízí pro toto specifické sekvenční stahování souborů výraznou rychlostní výhodu a přináší extra security setup.
Technicky existuje /batch endpoint, který ti umožní requesty sdružovat. Avšak parsovat multipart odpovědi pro komplexní binární soubory, jako je třeba Excel, je v M docela složité a nepřináší to dostatečný nárůst výkonu, aby to ospravedlnilo složitost. Také jsem chtěl upřednostnit snadnost použití ohledně autorizace. Řešení níže spoléhá na vestavěnou autentizaci organizačním účtem, kterou už používáš pro SharePoint.Contents, což tě uštědří od stavění vlastního OAuth2 flow.
Strategie je přímočará. Budeme předpokládat, že všechna tvá cílová data leží uvnitř specifické složky a nevyžadují komplexní filtrování. V případě, že to filtrování potřebuješ, můžeš jednoduše expandovat více polí a provést jakékoli úpravy po načtení metadat souboru. Vylistujeme všechny soubory uvnitř cílové složky, abychom získali jejich ServerRelativeUrl. Poté, podobně jako nativní konektory, stáhneme obsah jeden po druhém pomocí GetFileByServerRelativeUrl. Tento přístup minimalizuje počet requestů, protože přeskakujeme iterativní navigaci složku po složce a skáčeme přímo ke zdroji.
let
// config here
tenantUrl = "https://tenant.sharepoint.com",
siteUrlPath = "sites/SiteName",
targetFolderServerRelativeUrl = "/sites/SiteName/Shared Documents/TargetFolder",
getListOfFiles =
Json.Document(
Web.Contents(tenantUrl, [
RelativePath = siteUrlPath & "/_api/web/GetFolderByServerRelativeUrl('" & targetFolderServerRelativeUrl & "')/Files",
Headers = [Accept="application/json;odata=nometadata"]
]
)
),
extractServerRelativeUrlAndBuffer = List.Transform(getListOfFiles[value], each [ServerRelativeUrl])),
retrieveContentForFile = (serverRelativeUrl as text) =>
let
binaryContent = Web.Contents(tenantUrl, [
RelativePath = siteUrlPath & "/_api/web/GetFileByServerRelativeUrl('" & serverRelativeUrl & "')/$value"
]
),
// transformations here
processedData = binaryContent
in
processedData,
combineAllFiles = Table.Combine(List.Transform(extractServerRelativeUrlAndBuffer, each retrieveContentForFile(_)))
in
combineAllFilesVe zkratce, začneš voláním GetFolderByServerRelativeUrl na tvou cílovou složku. To načte seznam až 5 000 souborů. Ber na vědomí, že pokud máš více souborů, request selže, protože tento konkrétní endpoint nepodporuje stránkování.
Z této odpovědi extrahuješ ServerRelativeUrl pro každý soubor. Poté pro každou položku v seznamu zavoláš osvědčený endpoint GetFileByServerRelativeUrl. Ten vrátí binární data, což ti umožní provést jakékoli nezbytné transformace přímo uvnitř smyčky.
Pokud chceš nejlepší možný výkon, můžeš experimentovat s Binary.Buffer nebo ladit možnosti Excelu jako useHeaders či delayTypes. Můžeš také zkusit transformovat list na tabulku a načítat obsah skrze nový sloupec, což může dosáhnout vyšší concurrency. Nicméně měj na paměti, že toto jsou vysoce experimentální optimalizace a výsledky se mohou lišit. Ale co, alespoň teď znáš manuální alternativu k základnímu konektoru a můžeš znít víc cool před svými kolegy.
Shrnutí
Pro mě osobně bylo psaní tohoto blog postu několikahodinovým bojem s renderovací a stránkovací logikou SharePoint.Files, a to vše při snaze pochopit to "proč" za tím vším. Doufám, že sis užil tuhle destilovanou verzi.
Závěrem,, SharePoint.Contents with ApiVersion 15 je často dostatečující. Avšak pokud plánuješ provádět jakékoli úpravy na základě metadat souborů, toto vlastní řešení může být rychlejší a naučíš se při tom jednu nebo dvě věci.
Rozhodně tento blog sleduj. Stále mám v úmyslu vyřešit výzvu hromadného stahování ze SharePointu, protože musí existovat lepší způsob, i v říši Power Query.




Comments