tags:

views:

1006

answers:

3

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)?

+1  A: 

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.

Marc Gravell
That's an idea. I have though about the on-the-fly custom solution, but was afraid it wouldn't perform well. Is SourceForge not just using SVN? Or do they have their own software which just bridges SVN?The offline solution is too much of a configuration, I'm afraid. I want to generate a zipfile from any directory I want.If there is a good alternative to SVN which can do this, I'm happy to switch.
taoufik
A: 

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

jeffamaphone
I didn't know this, thanks for that. But it is not the solution to my problem. I'm really looking for a server-side solution to be served via the web. Like SourceForge/Codeplex do that.
taoufik
+3  A: 

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;
            }
        }
    }
}
taoufik