1. 程式人生 > >通過 HTTP POST 上傳檔案到伺服器

通過 HTTP POST 上傳檔案到伺服器

2. 上傳之前使用者無法預知上傳檔案的數目.
3. 因為是 ASP.NET 應用, 客戶端可能沒有裝 .NET Framework.
其實,我們知道.如果要跟 IE 端客戶檔案系統互動的話,程式碼必須在客戶端執行. 這個時候我們可以寫一個 Activex 控制元件來實現選擇資料夾和上傳.

一般我們常用兩種方式往服務端上傳檔案

1. FTP , 可以呼叫一些現成的 FTP 元件, 在 VB 裡面可以呼叫 Internet Transfer Control

2. HTTP , 使用 HTTP Post application/octet-stream 格式的位元組流給服務端.

FTP 很容易實現,我們不予考慮,著重提一下HTTP 的方式.



我們在單個檔案上傳的時候,一般都有以下的程式碼:

<%@ Page Language="C#" AutoEventWireup="True" %>

<HTML>
<head>

<script runat="server">

void Button1_Click(object sender, EventArgs e)
{

// Get the HTMLInputFile control from the Controls collection
// of the PlaceHolder control.
HTMLInputFile file = (HTMLInputFile)Place.FindControl("File1");

// Make sure a file was submitted.
if (Text1.Value == "")
{
Span1.InnerHTML = "Error: you must enter a file name";
return;
}

// Save file to server.
if (file.PostedFile != null)
{
try
{
file.PostedFile.SaveAs("c://temp//"+Text1.Value);
Span1.InnerHTML = "File uploaded successfully to " +
"<b>c://temp//" + Text1.Value + "</b> on the Web server";
}
catch (Exception exc)
{
Span1.InnerHTML = "Error saving file <b>c://temp//" +
Text1.Value + "</b><br>" + exc.ToString();
}
}
}

void Page_Load(object sender, EventArgs e)
{

// Create a new HTMLInputFile control.
HTMLInputFile file = new HTMLInputFile();
file.ID = "File1";

// Add the control to the Controls collection of the
// PlaceHolder control.
Place.Controls.Clear();
Place.Controls.Add(file);

}

</script>

</head>
<body>

<h3>HTMLInputFile Constructor Example</h3>

<form enctype="multipart/form-data" runat="server">

Specify the file to upload:
<ASP:PlaceHolder id="Place" runat="server"/>

<p>
Save as file name (no path):
<input id="Text1"
type="text"
runat="server">

<p>
<span id=Span1
style="font: 8pt verdana;"
runat="server" />

<p>
<input type=button
id="Button1"
value="Upload"
OnServerClick="Button1_Click"
runat="server">

</form>

</body>
</HTML>

程式碼有兩部分操作:

1. 客戶端, 通過使用者選擇要上傳的檔案, post 到服務端,這個時候注意 Post 的不是一些普通的表單,而是一個octet-stream 格式的流.

2. 服務端, 如果服務端 是 ASP.NET 處理程式的話, Request 物件有一個 Files 熟悉個您, Files 裡面會包含你上傳的各個檔案內容和檔案資訊.

你需要做的就是呼叫一下 File.Save ,把檔案複製到你的伺服器目錄.

我們模擬一下這個過程:

對於客戶端, 我們寫一個 windows 應用程式.

主要是以下幾行程式碼:

Dim wc As New System.Net.WebClient
wc.Credentials = New System.Net.NetworkCredential("username", "password", "domain")
wc.UploadFile("http://localhost/ASPnet/UPloadFile/WebForm1.ASPx", "c:/localfile.txt")

注意: 很多人用 UploadFile ,希望傳兩個引數,第一個是伺服器檔案存放的位置, 第二個是本地檔案路徑.

這個時候, 如果你改為wc.UploadFile("http://localhost/remotefile.txt", "c:/LocalFile.txt"), 一般會報錯 405, “Methods are not allowed“, 錯誤有一點誤導. 主要是告訴你 Remotefile.txt 無法處理你的 Post 方法. 而 IIS 的log 也會有類似的提示:

2004-08-13 03:37:57 127.0.0.1 POST /remotefile.txt - 80 - 127.0.0.1 - 405 0 1

所以這個時候明確一點, 服務端必須有一個檔案能夠處理這個post 過去的資料.



模擬服務端:

服務端就很簡單了, 跟上傳程式碼差不多.

新建一個 web form, HTML 程式碼如下.


<form id="Form1" encType="multipart/form-data" runat="server">

</form>

邏輯程式碼:

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'在此處放置初始化頁的使用者程式碼
Dim b() As Byte = Request.BinaryRead(Request.ContentLength)

Dim fs As New System.IO.FileStream("c:/k.txt", IO.FileMode.Append) //把request 請求的資料dump 到一個檔案中
Dim sw As New System.IO.BinaryWriter(fs)
sw.Write(b)
sw.Close()
fs.Close()

Request.Files(0).SaveAs("C:/t/" & Request.Files(0).FileName) //另存客戶端傳上來的檔案

End Sub


執行這個程式. 你會發現客戶端發過去的 請求,起始很有規則:


-----------------------8c64f47716481f0 //時間戳

Content-Disposition: form-data; name="file"; filename="a.txt" //檔名

Content-Type: application/octet-stream



//檔案的內容

-----------------------8c64f47716481f0

這個是.NET 測試的一些結果. 起始我們看一下 WebClient.UploadFile , 他起始內部就是整理形成一個 request 流.以下是對 webclient.uploadFile 反編譯後看到的結果.

public byte[] UploadFile(string address, string method, string fileName)
{
string text1;
string text2;
WebRequest request1;
string text3;
byte[] buffer1;
byte[] buffer2;
long num1;
byte[] buffer3;
int num2;
WebResponse response1;
byte[] buffer4;
DateTime time1;
long num3;
string[] textArray1;
FileStream stream1 = null;
try
{
fileName = Path.GetFullPath(fileName);
time1 = DateTime.Now;
num3 = time1.Ticks;
text1 = "---------------------" + num3.ToString("x");
if (this.m_headers == null)
{
this.m_headers = new WebHeaderCollection();
}
text2 = this.m_headers["Content-Type"];
if (text2 != null)
{
if (text2.ToLower(CultureInfo.InvariantCulture).StartsWith("multipart/"))
{
throw new WebException(SR.GetString("net_webclient_Multipart"));訪問地址 http://www.vlan9.com/net-
protocol/x106968.html