Is there a built in way to download a file to a local directory using HTTP?
I can shell out to wget or write a custom task, but I wanted to make sure there wasn't an existing way to accomplish this.
Thanks in advance!
The Microsoft Build Engine is a platform for building applications. This engine, which is also known as MSBuild, provides an XML schema for a project file that controls how the build platform processes and builds software.
At the command line, type MSBuild.exe <SolutionName>. sln , where <SolutionName> corresponds to the file name of the solution that contains the target that you want to execute. Specify the target after the -target: switch in the format <ProjectName>:<TargetName>.
proj files (which I am aware are actually MSBuild files).
In MSBuild 4.0 you can use inline tasks to avoid needing to compile and deploy a custom task in a separate assembly.
<UsingTask TaskName="DownloadFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll"> <ParameterGroup> <Address ParameterType="System.String" Required="true"/> <FileName ParameterType="System.String" Required="true" /> </ParameterGroup> <Task> <Reference Include="System" /> <Code Type="Fragment" Language="cs"> <![CDATA[ new System.Net.WebClient().DownloadFile(Address, FileName); ]]> </Code> </Task> </UsingTask> <Target Name="DownloadSomething"> <DownloadFile Address="http://somewebsite/remotefile" FileName="localfilepath" /> </Target>
MSBuild Community Tasks has a task WebDownload which seems to be what you require.
If you're trying to download a file that requires authentication (such as TFS Web, or an IIS server attached to a domain), neither the MSBuild Extension Pack nor the MSBuild Community Tasks seem to have the ability to give a username or password to the HTTP server. In this case, I ended up writing a custom MSBuild task. Here's what I did.
I followed the advice of Stack Overflow user Doug, in his answer for Download a file which requires authentication using vb.net/c#?, in which he suggests some code to add to a method written by Tom Archer on the Code Guru web site.
So I used MS Visual Studio 2010 to create a new C# project with the following code to create an MSBuild target named Wget (full source code shown):
// Include references to the following frameworks in your solution:
// - Microsoft.Build.Framework
// - Microsoft.Build.Utilities.v4.0
// - System
// - System.Net
using System;
using System.Net;
using System.IO;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace Wget
{
public class Wget: Task
{
[Required]
public String Address // HTTP address to access
{ get; set; }
[Required]
public String LocalFilename // Local file to which the downloaded page will be saved
{ get; set; }
public String Username // Credential for HTTP authentication
{ get; set; }
public String Password // Credential for HTTP authentication
{ get; set; }
public override bool Execute()
{
int read = DownloadFile(Address, LocalFilename, Username, Password);
Console.WriteLine("{0} bytes written", read);
return true;
}
public static int DownloadFile(String remoteFilename, String localFilename, String httpUsername, String httpPassword)
{
// Function will return the number of bytes processed
// to the caller. Initialize to 0 here.
int bytesProcessed = 0;
// Assign values to these objects here so that they can
// be referenced in the finally block
Stream remoteStream = null;
Stream localStream = null;
WebResponse response = null;
// Use a try/catch/finally block as both the WebRequest and Stream
// classes throw exceptions upon error
try
{
// Create a request for the specified remote file name
WebRequest request = WebRequest.Create(remoteFilename);
if (request != null)
{
// If a username or password have been given, use them
if (httpUsername.Length > 0 || httpPassword.Length > 0)
{
string username = httpUsername;
string password = httpPassword;
request.Credentials = new System.Net.NetworkCredential(username, password);
}
// Send the request to the server and retrieve the
// WebResponse object
response = request.GetResponse();
if (response != null)
{
// Once the WebResponse object has been retrieved,
// get the stream object associated with the response's data
remoteStream = response.GetResponseStream();
// Create the local file
localStream = File.Create(localFilename);
// Allocate a 1k buffer
byte[] buffer = new byte[1024];
int bytesRead;
// Simple do/while loop to read from stream until
// no bytes are returned
do
{
// Read data (up to 1k) from the stream
bytesRead = remoteStream.Read(buffer, 0, buffer.Length);
// Write the data to the local file
localStream.Write(buffer, 0, bytesRead);
// Increment total bytes processed
bytesProcessed += bytesRead;
} while (bytesRead > 0);
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Close the response and streams objects here
// to make sure they're closed even if an exception
// is thrown at some point
if (response != null) response.Close();
if (remoteStream != null) remoteStream.Close();
if (localStream != null) localStream.Close();
}
// Return total bytes processed to caller.
return bytesProcessed;
}
}
}
With that in place, I can add the following task to my MSBuild project:
<!-- Get the contents of a Url-->
<Wget
Address="http://mywebserver.com/securepage"
LocalFilename="mydownloadedfile.html"
Username="myusername"
Password="mypassword">
</Wget>
The Wget task downloads the page served by mywebserver.com and saves it to a file in the current working directory as mydownloadedfile.html, using the username "myusername" and password "mypassword".
However, in order to use the custom Wget MSBuild task, I must tell MSBuild where to find the Wget assembly file (.dll). This is done with MSBuild's element:
<!-- Import your custom MSBuild task -->
<UsingTask AssemblyFile="MyCustomMSBuildTasks\Wget\bin\Release\Wget.dll" TaskName="Wget" />
If you want to get fancy, you can even have your MSBuild project build Wget before it's called. To do that, build the solution with the <MSBuild Projects>
task, and import it with the <UsingTaks AssemblyFile>
task, something like this:
<!-- Build the custom MSBuild target solution-->
<MSBuild Projects="MyCustomMSBuildTasks\CustomBuildTasks.sln" Properties="Configuration=Release" />
<!-- Import your custom MSBuild task -->
<UsingTask AssemblyFile="MyCustomMSBuildTasks\Wget\bin\Release\Wget.dll" TaskName="Wget" />
<!-- Get the contents of a Url-->
<Wget
Address="http://mywebserver.com/securepage"
LocalFilename="mydownloadedfile.html"
Username="myusername"
Password="mypassword">
</Wget>
If you've never created a custom MSBuild target before, it's not too difficult -- once you know the basics. Look at the C# code above, take a look at the official MSDN documentation, and search around on the web for more examples. A good place to start is:
The DownloadFile task is available in MSBuild 15.8 and above (since August 14, 2018)
example:
<PropertyGroup>
<LicenceUrl>https://raw.githubusercontent.com/Microsoft/msbuild/master/LICENSE</LicenceUrl>
</PropertyGroup>
<Target Name="DownloadContentFiles" BeforeTargets="Build">
<DownloadFile
SourceUrl="$(LicenceUrl)"
DestinationFolder="$(MSBuildProjectDirectory)">
<Output TaskParameter="DownloadedFile" ItemName="Content" />
</DownloadFile>
</Target>
For more details: DownloadFile task
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With