Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed #271: Added zip controller to easily upload/download files
- Loading branch information
davidebbo
committed
Feb 11, 2013
1 parent
0264f93
commit 66d1a51
Showing
10 changed files
with
233 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using System; | ||
using System.IO; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Headers; | ||
using Kudu.Client.Infrastructure; | ||
|
||
namespace Kudu.Client.Editor | ||
{ | ||
public class RemoteZipManager : KuduRemoteClientBase | ||
{ | ||
public RemoteZipManager(string serviceUrl, ICredentials credentials = null, HttpMessageHandler handler = null) | ||
: base(serviceUrl, credentials, handler) | ||
{ | ||
} | ||
|
||
public Stream GetZipStream(string path) | ||
{ | ||
HttpResponseMessage response = Client.GetAsync(path).Result; | ||
if (response.StatusCode == HttpStatusCode.NotFound) | ||
{ | ||
return null; | ||
} | ||
|
||
return response | ||
.EnsureSuccessful() | ||
.Content | ||
.ReadAsStreamAsync() | ||
.Result; | ||
} | ||
|
||
public void PutZipStream(string path, Stream zipFile) | ||
{ | ||
using (var request = new HttpRequestMessage()) | ||
{ | ||
request.Method = HttpMethod.Put; | ||
request.RequestUri = new Uri(path, UriKind.Relative); | ||
request.Headers.IfMatch.Add(EntityTagHeaderValue.Any); | ||
request.Content = new StreamContent(zipFile); | ||
Client.SendAsync(request).Result.EnsureSuccessful(); | ||
} | ||
} | ||
|
||
|
||
public void PutZipFile(string path, string localZipPath) | ||
{ | ||
using (var stream = File.OpenRead(localZipPath)) | ||
{ | ||
PutZipStream(path, stream); | ||
} | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
using System.IO; | ||
using Ionic.Zip; | ||
using Kudu.TestHarness; | ||
using Xunit; | ||
|
||
namespace Kudu.FunctionalTests | ||
{ | ||
public class ZipTests | ||
{ | ||
[Fact] | ||
public void TestZipController() | ||
{ | ||
ApplicationManager.Run("TestZip", appManager => | ||
{ | ||
string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); | ||
Directory.CreateDirectory(tempDirectory); | ||
string tempZipPath = Path.GetTempFileName(); | ||
try | ||
{ | ||
// Create file on server using vfs | ||
appManager.VfsWebRootManager.WriteAllText("foo.txt", "Hello zip"); | ||
// Make sure it's part of the zip we get | ||
using (var zipFile = ZipFile.Read(appManager.ZipManager.GetZipStream("site"))) | ||
{ | ||
zipFile.ExtractAll(tempDirectory); | ||
string settings = File.ReadAllText(Path.Combine(tempDirectory, "wwwroot", "foo.txt")); | ||
Assert.Contains("Hello zip", settings); | ||
} | ||
string barFile = Path.Combine(tempDirectory, "wwwroot", "bar.txt"); | ||
File.WriteAllText(barFile, "Kudu zip"); | ||
using (var zipFile2 = new ZipFile()) | ||
{ | ||
zipFile2.AddDirectory(tempDirectory); | ||
zipFile2.Save(tempZipPath); | ||
} | ||
// Upload a zip with an additional file | ||
appManager.ZipManager.PutZipFile("site", tempZipPath); | ||
// Use vfs to make sure it's there | ||
string barContent = appManager.VfsWebRootManager.ReadAllText("bar.txt"); | ||
Assert.Contains("Kudu zip", barContent); | ||
} | ||
finally | ||
{ | ||
Directory.Delete(tempDirectory, recursive: true); | ||
File.Delete(tempZipPath); | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
using System.IO; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Headers; | ||
using System.Threading.Tasks; | ||
using Ionic.Zip; | ||
using Kudu.Contracts.Tracing; | ||
using Kudu.Core; | ||
using Kudu.Services.Infrastructure; | ||
|
||
namespace Kudu.Services.Deployment | ||
{ | ||
// Extending VfsControllerBase is a slight abuse since this has nothing to do with vfs. But there is a lot | ||
// of good reusable logic in there. We could consider extracting a more basic base class from it. | ||
public class ZipController : VfsControllerBase | ||
{ | ||
public ZipController(ITracer tracer, IEnvironment environment) | ||
: base(tracer, environment, environment.RootPath) | ||
{ | ||
} | ||
|
||
protected override Task<HttpResponseMessage> CreateDirectoryGetResponse(DirectoryInfo info, string localFilePath) | ||
{ | ||
HttpResponseMessage response = Request.CreateResponse(); | ||
using (var zip = new ZipFile()) | ||
{ | ||
foreach (FileSystemInfo fileSysInfo in info.EnumerateFileSystemInfos()) | ||
{ | ||
bool isDirectory = (fileSysInfo.Attributes & FileAttributes.Directory) != 0; | ||
|
||
if (isDirectory) | ||
{ | ||
zip.AddDirectory(fileSysInfo.FullName, fileSysInfo.Name); | ||
} | ||
else | ||
{ | ||
// Add it at the root of the zip | ||
zip.AddFile(fileSysInfo.FullName, "/"); | ||
} | ||
} | ||
|
||
using (var ms = new MemoryStream()) | ||
{ | ||
zip.Save(ms); | ||
response.Content = ms.AsContent(); | ||
} | ||
} | ||
|
||
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); | ||
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); | ||
|
||
// Name the zip after the folder. e.g. "c:\foo\bar\" --> "bar" | ||
response.Content.Headers.ContentDisposition.FileName = Path.GetFileName(Path.GetDirectoryName(localFilePath)) + ".zip"; | ||
return Task.FromResult(response); | ||
} | ||
|
||
protected override Task<HttpResponseMessage> CreateItemGetResponse(FileSystemInfo info, string localFilePath) | ||
{ | ||
// We don't support getting a file from the zip controller | ||
// Conceivably, it could be a zip file containing just the one file, but that's rarely interesting | ||
HttpResponseMessage notFoundResponse = Request.CreateResponse(HttpStatusCode.NotFound); | ||
return Task.FromResult(notFoundResponse); | ||
} | ||
|
||
protected override async Task<HttpResponseMessage> CreateDirectoryPutResponse(DirectoryInfo info, string localFilePath) | ||
{ | ||
using (var zipFile = ZipFile.Read(await Request.Content.ReadAsStreamAsync())) | ||
{ | ||
// The unzipping is done over the existing folder, without first removing existing files. | ||
// Hence it's more of a PATCH than a PUT. We should consider supporting both with the right semantic. | ||
// Though a true PUT at the root would be scary as it would wipe all existing files! | ||
zipFile.ExtractAll(localFilePath, ExtractExistingFileAction.OverwriteSilently); | ||
} | ||
|
||
return Request.CreateResponse(HttpStatusCode.OK); | ||
} | ||
|
||
protected override Task<HttpResponseMessage> CreateItemPutResponse(FileSystemInfo info, string localFilePath, bool itemExists) | ||
{ | ||
// We don't support putting an individual file using the zip controller | ||
HttpResponseMessage notFoundResponse = Request.CreateResponse(HttpStatusCode.NotFound); | ||
return Task.FromResult(notFoundResponse); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters