Very simple using DotNetZip.
[System.Reflection.Assembly]::LoadFrom("c:\\dev\dotnet\\Ionic.Zip.dll");
$directoryToZip = "c:\\temp";
$zipfile = new-object Ionic.Zip.ZipFile;
$zipfile.AddEntry("Readme.txt", "This is a zipfile created from within powershell.")
$zipfile.AddDirectory($directoryToZip, "home")
$zipfile.Save("ZipFiles.ps1.out.zip");
$zipfile.Dispose();
The ZipFile object does the recursion for you, as part of the ZipFile.AddDirectory() method.
DotNetZip is a free library.
One add-on comment - I had reason to do FTP uploads in the past, as part of a software release. I found that on successive uploads, most of the files had not changed. So I introduced a "table of contents" file, which mapped the MD5 hash of each file to its name. Before uploading, I'd download the table of contents, then check each MD5 in the table against the MD5 of the file-on-disk. If they match, then no need to upload that particular file. This saved lots of time and data transfer.
I don't have a powershell script for that, though. :<