O365中删除空文档库的程序化实现方案求助
Hey there! Let's tackle your problem step by step. First, let's fix that frustrating "underlying connection was closed" error you're hitting, then dive into reliable scripts to identify and delete empty SharePoint Online (O365) document libraries.
Fixing the "Underlying Connection Was Closed" Error
This error almost always stems from outdated security protocols—SharePoint Online requires TLS 1.2, and if your script isn't enforcing it, the connection gets dropped. Here's how to fix it:
- For PowerShell: Add this line at the very start of your script:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - For CSOM (C#): Set the protocol before initializing your client context:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Also, make sure you're using the latest version of the SharePoint Online PowerShell module or CSOM NuGet package—old versions often have compatibility issues with modern SharePoint endpoints.
PowerShell Script to Identify & Delete Empty Document Libraries
This script will loop through all site collections you have access to, check each document library for content (items + subfolders), and let you safely delete empty ones (with a confirmation prompt to avoid mistakes).
# Enforce TLS 1.2 to fix connection issues [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 # Connect to your SharePoint Online admin center Connect-SPOService -Url "https://yourtenant-admin.sharepoint.com" # Fetch all site collections in your tenant $sites = Get-SPOSite -Limit All foreach ($site in $sites) { Write-Host "Processing site: $($site.Url)" -ForegroundColor Cyan # Get only visible document libraries (BaseTemplate 101 = Document Library) $libraries = Get-SPOList -WebUrl $site.Url | Where-Object { $_.BaseTemplate -eq 101 -and !$_.Hidden } foreach ($lib in $libraries) { # Check if the library is empty: ItemCount is 0, and only the root folder exists $rootFolder = Get-SPOFolder -List $lib -WebUrl $site.Url $subfolderCount = ($rootFolder.Folders).Count if ($lib.ItemCount -eq 0 -and $subfolderCount -eq 0) { Write-Host "Found empty library: $($lib.Title) at $($site.Url)/$($lib.RootFolder.ServerRelativeUrl)" -ForegroundColor Yellow # Confirm before deletion (remove this line if you're fully confident in testing) $confirm = Read-Host "Delete this library? (Y/N)" if ($confirm -match "^[Yy]$") { Remove-SPOList -Identity $lib -WebUrl $site.Url -Force Write-Host "Successfully deleted library: $($lib.Title)" -ForegroundColor Green } } } } # Clean up the connection Disconnect-SPOService
Key Notes:
- Replace
https://yourtenant-admin.sharepoint.comwith your actual tenant admin URL. - The check for
$subfolderCount -eq 0accounts for the root folder that's always present in libraries—if there are no subfolders or items, the library is truly empty. - Always test this on a non-production site first! Remove the confirmation prompt only after verifying the script works as expected.
CSOM C# Example for Automated Cleanup
If you prefer C# for more control, this CSOM code does the same job. First, install the Microsoft.SharePointOnline.CSOM NuGet package in your project.
using Microsoft.SharePoint.Client; using System; using System.Net; using System.Security; namespace SharePointEmptyLibraryCleanup { class Program { static void Main(string[] args) { // Enforce TLS 1.2 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // Replace these with your tenant details string tenantAdminUrl = "https://yourtenant-admin.sharepoint.com"; string adminUsername = "your-admin@yourtenant.onmicrosoft.com"; string adminPassword = "your-secure-password"; // Use secure input in production! // Connect to the admin center to get all site collections using (ClientContext adminContext = new ClientContext(tenantAdminUrl)) { // Authenticate (use Azure AD app auth for production instead of plain text) SecureString securePassword = new SecureString(); foreach (char c in adminPassword) securePassword.AppendChar(c); adminContext.Credentials = new SharePointOnlineCredentials(adminUsername, securePassword); // Load all site properties var siteProperties = adminContext.SiteProperties; adminContext.Load(siteProperties); adminContext.ExecuteQuery(); foreach (var siteProp in siteProperties) { Console.WriteLine($"Processing site: {siteProp.Url}"); // Connect to the individual site collection using (ClientContext siteContext = new ClientContext(siteProp.Url)) { siteContext.Credentials = adminContext.Credentials; // Load all visible document libraries var libraries = siteContext.Web.Lists; siteContext.Load(libraries, libs => libs.Include( lib => lib.Title, lib => lib.ItemCount, lib => lib.RootFolder, lib => lib.Hidden, lib => lib.BaseTemplate)); siteContext.ExecuteQuery(); foreach (var lib in libraries) { if (lib.BaseTemplate == 101 && !lib.Hidden) { // Load subfolders in the root to check for emptiness siteContext.Load(lib.RootFolder.Folders); siteContext.ExecuteQuery(); if (lib.ItemCount == 0 && lib.RootFolder.Folders.Count == 0) { Console.WriteLine($"Empty library found: {lib.Title} at {siteProp.Url}{lib.RootFolder.ServerRelativeUrl}"); // Confirm deletion (remove for fully automated runs) Console.Write("Delete this library? (Y/N): "); string confirm = Console.ReadLine(); if (confirm.Equals("Y", StringComparison.OrdinalIgnoreCase)) { lib.DeleteObject(); siteContext.ExecuteQuery(); Console.WriteLine($"Deleted library: {lib.Title}"); } } } } } } } } } }
Production Best Practices:
- Never hardcode passwords—use Azure AD app registration with client credentials for unattended runs, or prompt users for secure input.
- Add try/catch blocks to handle connection timeouts, permission errors, or unexpected site structures (critical when processing thousands of sites).
- Log all actions (which libraries were checked, deleted, skipped) for auditing purposes.
内容的提问来源于stack exchange,提问作者Jim Wooley

