X893

Download file with non-US name from CommunityServer

Current version of CS use Content-disposition filename header field to set correct filename during download.
This work correctly on IE 7/8 and not work on all other browsers.
This browser not UrlEncode filename field from Content-disposition field.
Simply change next httphandlers (web.config) to support national filenames.

<system.web>
	...
	<httpHandlers>
		...
		<add verb="GET" path="cfs-file.ashx" type="CustomCSHandlers.FileHttpHandler, CustomCSHandlers" />
		<add verb="GET" path="cfs-filesystemfile.ashx" type="CustomCSHandlers.FileSystemFileStorageHttpHandler, CustomCSHandlers" />
		...
	</httpHandlers>
	...
</system.web>


Create C# class library project CustomCSHandlers and add 2 files.

1.FileHttpHandler.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using CommunityServer.Components;

namespace CustomCSHandlers
{
	public class FileHttpHandler : IHttpHandler
	{
		// Requested URLS are in the format:  .../__key/filestorekey/path/filename

		public bool IsReusable
		{
			get { return true; }
		}

		public void ProcessRequest(HttpContext context)
		{
			CSContext.Current.AllowTokenRequests(false);

			ICentralizedFile file = CentralizedFileStorageProvider.GetCentralizedFileByUrl(context.Request.Url.ToString());
			if (file != null)
			{
				context.Response.Cache.SetAllowResponseInBrowserHistory(true);
				context.Response.Cache.SetValidUntilExpires(true);
				context.Response.Cache.SetExpires(DateTime.Now.AddDays(1));
				context.Response.Cache.SetLastModified(DateTime.Now);

				if (CentralizedFileStorageProvider.HasAccessValidator(file.FileStoreKey))
					context.Response.Cache.SetCacheability(HttpCacheability.Private);
				else
					context.Response.Cache.SetCacheability(HttpCacheability.Public);

				if (CentralizedFileStorageProvider.CurrentUserHasAccess(file.FileStoreKey, file.Path, file.FileName))
				{
					string path = file.GetDownloadUrl();
					int i;
					if ((i = path.LastIndexOf("/")) > 0)
					{
						string filename = Globals.UrlDecodeFileComponent(path.Substring(i + 1));
						path = path.Substring(0, i + 1) + "N$/" + filename;
					}
					context.Response.RedirectLocation = Globals.FullPath(path);
					context.Response.StatusCode = 301;
				}
				else
					context.Response.StatusCode = 403;
			}
			else
				context.Response.StatusCode = 404;
		}
	}
}

2. FileSystemFileStorageHttpHandler.cs

using System;
using System.Collections.Generic;
using System.Text;
using CommunityServer.Components;
using System.IO;
using System.Web;

namespace CustomCSHandlers
{
	public class FileSystemFileStorageHttpHandler : IHttpHandler
	{
		// Requested URLS are in the format:  ...__key/[filestorekey]/[path]/[filename]

		public bool IsReusable
		{
			get { return true; }
		}

		protected CommunityServer.Components.FileSystemFileStorageFile GetFileFromPath(string path)
		{
			int index = path.IndexOf("__key/");
			if (index < 0)
				return null;

			path = path.Substring(index + 6);

			index = path.IndexOf('/');
			if (index < 0)
				return null;

			string fileStoreKey = path.Substring(0, index);

			index = path.LastIndexOf('/');
			if (index < 0)
				return null;

			string fileName = path.Substring(index + 1);

			if (path.Length - (fileStoreKey.Length + fileName.Length + 2) <= 0)
				path = string.Empty;
			else
				path = path.Substring(fileStoreKey.Length + 1, path.Length - (fileStoreKey.Length + fileName.Length + 2));

			fileStoreKey = Globals.UrlDecodePathComponent(fileStoreKey);
			if (path.EndsWith("/N$"))

path = path.Substring(0, path.Length - 3);

else if (path == "N$")

path = path.Substring(0, path.Length - 2); else fileName = Globals.UrlDecodeFileComponent(fileName); path = Globals.UrlDecodePathComponent(path); if (string.IsNullOrEmpty(fileStoreKey) || string.IsNullOrEmpty(fileName)) return null; CommunityServer.Components.FileSystemFileStorageProvider fileProvider = CentralizedFileStorageProvider.Instance(fileStoreKey) as CommunityServer.Components.FileSystemFileStorageProvider; if (fileProvider != null) return fileProvider.GetFile(path, fileName) as CommunityServer.Components.FileSystemFileStorageFile; else return null; } public void ProcessRequest(HttpContext context) { CSContext.Current.AllowTokenRequests(false); CommunityServer.Components.FileSystemFileStorageFile file = GetFileFromPath(context.Request.Path); if (file == null) { context.Response.StatusCode = 404; return; } DateTime lastModified = (new FileInfo(file.FullLocalPath)).LastWriteTime; DateTime currentLastModifiedDate = DateTime.MinValue; #region If-Modified-Since DateTime.TryParse(context.Request.Headers["If-Modified-Since"] ?? "", out currentLastModifiedDate); if (Math.Abs(((TimeSpan)lastModified.ToUniversalTime().Subtract(currentLastModifiedDate.ToUniversalTime())).TotalSeconds) <= 1) { context.Response.StatusCode = 304; context.Response.Status = "304 Not Modified"; return; } #endregion #region If-None-Match (ETag) long eTag = 0; if (long.TryParse(context.Request.Headers["If-None-Match"] ?? "", out eTag)) { currentLastModifiedDate = new DateTime(eTag); if (lastModified == currentLastModifiedDate) { context.Response.StatusCode = 304; context.Response.Status = "304 Not Modified"; return; } } #endregion if (!CentralizedFileStorageProvider.CurrentUserHasAccess(file.FileStoreKey, file.Path, file.FileName)) { context.Response.StatusCode = 403; return; } context.Response.ContentType = CommunityServer.Configuration.MimeTypeConfiguration.GetMimeType(file.FileName); context.Response.Cache.SetAllowResponseInBrowserHistory(true); context.Response.Cache.SetLastModified(lastModified.ToUniversalTime()); context.Response.Cache.SetETag(lastModified.Ticks.ToString()); if (!CentralizedFileStorageProvider.HasAccessValidator(file.FileStoreKey)) context.Response.Cache.SetCacheability(HttpCacheability.Public); else context.Response.Cache.SetCacheability(HttpCacheability.Private); string disposition; if (context.Response.ContentType == "application/pdf" || context.Response.ContentType == "application/octet-stream") disposition = "attachment"; else disposition = "inline"; if (context.Request.Browser.Browser.IndexOf("Netscape") != -1) context.Response.AddHeader("Content-disposition", disposition + "; filename*0*=" + context.Server.UrlEncode(file.FileName).Replace("+", "%20") + ""); else { context.Response.AddHeader("Content-disposition", disposition); // filename=" + context.Server.UrlEncode(file.FileName).Replace("+", "%20") + ""); } // Send files stored on UNC paths explicitly to avoid a bug with TransmitFile. if (!file.FullLocalPath.StartsWith("\\")) context.Response.TransmitFile(file.FullLocalPath); else { context.Response.AddHeader("Content-Length", file.ContentLength.ToString("0")); using (Stream s = new FileStream(file.FullLocalPath, FileMode.Open, FileAccess.Read)) { context.Response.Buffer = false; context.Response.BufferOutput = false; try { byte[] buffer = new byte[64 * 1024]; int read; while ((read = s.Read(buffer, 0, buffer.Length)) > 0) { if (!context.Response.IsClientConnected) break; context.Response.OutputStream.Write(buffer, 0, read); context.Response.OutputStream.Flush(); } } catch (HttpException) { } catch (Exception ex) { new CSException(CSExceptionType.UnknownError, "FileSystemFileStorageFile", ex).Log(); } context.Response.Flush(); context.Response.Close(); s.Close(); } } } } }

add CustomCSHandlers.dll to bin folder.

0 comment(s) so far

Post your comment

Thanks for your comments

  • Comment

Github
Bitbucket
Codeplex
SF.net

Status

Subscribe to x893 blog Subscribe to x893 blog