3
votes

I've installed VisualSVN on my Windows 2003 server, and have configured it to provide anonymous read-access. From my understanding VisualSVN just uses apache and the official SVN Repository server underneath.

Now, I'd like to extend the SVN web page to provide "download HEAD as ZIP" functionality. Web portals like SourceForge and Codeplex do provide this functionality.

Is there a plugin for the SVN Repository server for this? Or may be a separate web client (preferably ASP.NET)?

3
Try posting on serverfault.comstukelly
@stukelly - disagree; SVN is more a "dev" thing than an it-pro thing.Marc Gravell
@stukelly - I don't have access to serverfault.com, and further, I'm a developer and I'm prepared to develop a good custom solution. So, I'm not only after off-the-shelf products.tofi9

3 Answers

5
votes

I found a solution, and would want to share it with you, in case someone else would want to achieve the same solution.

After analyzing WebSvn, I found out that they use the SVN Export Directory functionality to download the source to a local folder, and then zip the directory, on the fly. The performance is quite well.

My solution in C# below, is using SharpSVN and DotNetZip. The full source code can be found on my SVN repository.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpSvn;
using Ionic.Zip;
using System.IO;
using SharpSvn.Security;

namespace SvnExportDirectory
{
    public class SvnToZip
    {
        public Uri SvnUri { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }

        private bool passwordSupplied;

        public SvnToZip() { }

        public SvnToZip(Uri svnUri)
        {
            this.SvnUri = svnUri;
        }

        public SvnToZip(string svnUri)
            : this(new Uri(svnUri)) { }

        public void ToFile(string zipPath)
        {
            if (File.Exists(zipPath))
                File.Delete(zipPath);

            using (FileStream stream = File.OpenWrite(zipPath))
            {
                this.Run(stream);
            }
        }

        public MemoryStream ToStream()
        {
            MemoryStream ms = new MemoryStream();
            this.Run(ms);
            ms.Seek(0, SeekOrigin.Begin);
            return ms;
        }

        private void Run(Stream stream)
        {
            string tmpFolder =
                Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

            try
            {
                using (SvnClient client = new SvnClient())
                {
                    //client.Authentication.Clear();
                    client.Authentication.UserNamePasswordHandlers += Authentication_UserNamePasswordHandlers;

                    SvnUpdateResult res;
                    bool downloaded = client.Export(SvnTarget.FromUri(SvnUri), tmpFolder, out res);
                    if (downloaded == false)
                        throw new Exception("Download Failed");
                }

                using (ZipFile zipFile = new ZipFile())
                {
                    zipFile.AddDirectory(tmpFolder, GetFolderName());
                    zipFile.Save(stream);
                }
            }
            finally
            {
                if (File.Exists(tmpFolder))
                    File.Delete(tmpFolder);
            }
        }


        private string GetFolderName()
        {
            foreach (var potential in SvnUri.Segments.Reverse())
            {
                if (string.Equals(potential, "trunk", StringComparison.InvariantCultureIgnoreCase) == false)
                    return potential;
            }
            return null;
        }

        void Authentication_UserNamePasswordHandlers(object sender, SvnUserNamePasswordEventArgs e)
        {
            if (passwordSupplied)
            {
                e.Break = true;
            }
            else
            {
                if (this.Username != null)
                    e.UserName = this.Username;

                if (this.Password != null)
                    e.Password = this.Password;

                passwordSupplied = true;
            }
        }
    }
}
1
votes

I don't know about "built in", but you could try writing a page that uses SharpSVN and #ZipLib to do the job...

If that is too slow, you could presumably use either a commit hook or a scheduled job (every few minutes or something) to keep a pre-prepared zip handy somewhere that you can just return - using the "create as a different name then rename when ready" trick to minimize the amount of time it is locked / unavailable. Or name it with the revision number.

0
votes

TortoiseSVN registers some pluggable protocol that browsers will recognize. svn://blahblahblah... you could look into that. But then you have to use TortoiseSVN...