C# – Come inviare un file con HttpClient

 C Programming >> Programmazione C >  >> Tags >> File
C# – Come inviare un file con HttpClient

Per inviare un file in una richiesta con HttpClient, aggiungere il file in un oggetto MultipartFormDataContent e inviare questo oggetto come contenuto della richiesta. Ecco un esempio:

var filePath = @"C:\house.png";

using (var multipartFormContent = new MultipartFormDataContent())
{
	//Load the file and set the file's Content-Type header
	var fileStreamContent = new StreamContent(File.OpenRead(filePath));
	fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");

	//Add the file
	multipartFormContent.Add(fileStreamContent, name: "file", fileName: "house.png");

	//Send it
	var response = await httpClient.PostAsync("https://localhost:12345/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}
Code language: C# (cs)

Questo invia la seguente richiesta POST multipart/form-data:

POST https://localhost:12345/files/ HTTP/1.1
Host: localhost:12345
Content-Type: multipart/form-data; boundary="44b2ed38-1ac7-4731-b2f4-f84bf159748d"
Content-Length: 7279

--44b2ed38-1ac7-4731-b2f4-f84bf159748d
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=house.png; filename*=utf-8''house.png

<bytes>Code language: plaintext (plaintext)

In questo articolo, spiegherò alcuni dettagli su MultipartFormDataContent e mostrerò alcuni altri scenari di invio di file.

MultipartFormDataContent

Aggiungi() parametri

Quando aggiungi un file a MultipartFormDataContent, usi il seguente sovraccarico del metodo Add():

public void Add(HttpContent content, string name, string fileName);
Code language: C# (cs)

Il nome parametro è il nome del campo del modulo. Impostalo sul nome del parametro definito dall'API Web (se utilizza la mappatura automatica).

Il nomefile parametro è il nome del file originale.

Smaltimento

Quando elimini MultipartFormDataContent, elimina tutti gli oggetti HttpContent che hai aggiunto. Inoltre, quando si elimina StreamContent, viene eliminato il flusso di file sottostante. Grazie a questo smaltimento a cascata, è necessario solo un blocco di utilizzo (o utilizzando la dichiarazione se preferisci quello stile).

In breve, MultipartFormDataContent elimina l'oggetto StreamContent, che elimina l'oggetto FileStream.

Invio dei dati del modulo con più campi, incluso un file

Quando devi inviare un file, probabilmente dovrai associarlo a qualche entità. In altre parole, ti consigliamo di inviare altri campi insieme al file. Il modo più semplice per farlo è aggiungere tutto a MultipartFormDataContent.

Ad esempio, supponiamo che tu stia inviando un file e devi includere un titolo e un ID utente. Oltre ad aggiungere il file, puoi aggiungere i campi del titolo e dell'ID utente ai dati del modulo in questo modo:

var filePath = @"C:\house.png";

using (var multipartFormContent = new MultipartFormDataContent())
{
	//Add other fields
	multipartFormContent.Add(new StringContent("123"), name: "UserId");
	multipartFormContent.Add(new StringContent("Home insurance"), name: "Title");

	//Add the file
	var fileStreamContent = new StreamContent(File.OpenRead(filePath));
	fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	multipartFormContent.Add(fileStreamContent, name: "file", fileName: "house.png");

	//Send it
	var response = await httpClient.PostAsync("https://localhost:12345/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}
Code language: C# (cs)

Questo invia la seguente richiesta multipart/form-data. Si noti che includeva parti per i campi Titolo e UserId:

POST https://localhost:12345/files/ HTTP/1.1
Host: localhost:12345
Content-Type: multipart/form-data; boundary="00d335a2-0389-48e1-85d9-0daf70c2879e"
Content-Length: 7519

--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=UserId

123
--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=Title

Home insurance
--00d335a2-0389-48e1-85d9-0daf70c2879e
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=house.png; filename*=utf-8''house.png

<bytes>
Code language: plaintext (plaintext)

Invio di un array di byte

Se si dispone già di un array di byte e non è necessario caricare il file come flusso di file, è possibile utilizzare ByteArrayContent invece di StreamContent. Ecco un esempio:

using (var multipartFormContent = new MultipartFormDataContent())
{
	//Add the file as a byte array
	var byteContent = new ByteArrayContent(fileBytesFromDatabase);
	byteContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
	multipartFormContent.Add(byteContent, name: "file", fileName: "house.png");

	//Send it
	var response = await httpClient.PostAsync("https://localhost:12345/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}
Code language: C# (cs)

Questo genera la seguente richiesta:

POST https://localhost:12345/files/ HTTP/1.1
Host: localhost:12345
Content-Type: multipart/form-data; boundary="f4186b10-2cf4-4497-9a65-6e592d6cfce1"
Content-Length: 7243

--f4186b10-2cf4-4497-9a65-6e592d6cfce1
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=house.png; filename*=utf-8''house.png

 <bytes>Code language: plaintext (plaintext)

Invio di più file

Esistono due modi per inviare più file:

  • Invia più file utilizzando lo stesso nome parametro.
  • Invia ogni file con il proprio nome parametro.

L'opzione che scegli dipenderà da come è configurata l'API web. Ecco un esempio della prima opzione:inviare più file utilizzando lo stesso nome parametro:

var filePaths = new string[] { @"C:\house.png", @"C:\car.png" };

using (var multipartFormContent = new MultipartFormDataContent())
{
	foreach(var filePath in filePaths)
	{
		var fileName = Path.GetFileName(filePath);

		//Load the file and set the file's Content-Type header
		var fileStreamContent = new StreamContent(File.OpenRead(filePath));
		fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");

		//Add the file
		multipartFormContent.Add(fileStreamContent, name: "files", fileName: fileName);
	}

	//Send it
	var response = await httpClient.PostAsync("https://localhost:12345/files/", multipartFormContent);
	response.EnsureSuccessStatusCode();
	return await response.Content.ReadAsStringAsync();
}
Code language: C# (cs)

Questo invia la seguente richiesta:

POST https://localhost:12345/files/ HTTP/1.1
Host: localhost:12345
Content-Type: multipart/form-data; boundary="92f8b9da-896f-41ff-8709-85a0b8d0ef08"
Content-Length: 14442

--92f8b9da-896f-41ff-8709-85a0b8d0ef08
Content-Type: image/png
Content-Disposition: form-data; name=files; filename=house.png; filename*=utf-8''house.png

<bytes>

--92f8b9da-896f-41ff-8709-85a0b8d0ef08
Content-Type: image/png
Content-Disposition: form-data; name=files; filename=car.png; filename*=utf-8''car.png

<bytes>
Code language: plaintext (plaintext)

Si noti che ogni file viene inserito nella propria parte (separata dalla stringa limite).

Impostazione del tipo di contenuto del file

Il file immagine "house.png" ha un tipo di contenuto "image/png", che è stato aggiunto come intestazione del contenuto del file con la seguente riga:

fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
Code language: C# (cs)

Questo imposta l'intestazione Content-Type nella parte del file nella richiesta multiparte:

--f4186b10-2cf4-4497-9a65-6e592d6cfce1
Content-Type: image/png
Content-Disposition: form-data; name=file; filename=house.png; filename*=utf-8''house.png
Code language: plaintext (plaintext)

Se l'API Web con cui ti stai integrando richiede di impostare il tipo di contenuto del file, devi impostarlo in modo esplicito (non viene impostato automaticamente). Puoi impostarlo in base all'estensione del file (o codificarlo se appropriato). Ecco un esempio:

//Cached somewhere
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
	[".png"] = "image/png",
	[".jpg"] = "image/jpeg",
	[".gif"] = "image/gif"
};


var filePath = @"C:\house.png";

var extension = Path.GetExtension(filePath);

if (!map.TryGetValue(extension, out string contentType))
{
	throw new Exception("Can't send this type of file");
}

var fileStreamContent = new StreamContent(File.OpenRead(filePath));
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(contentType);
Code language: C# (cs)

Fare riferimento al codice sorgente .NET FileExtensionContentTypeProvider per un elenco completo dei mapping. Poiché quella classe è solo un wrapper per un Dictionary, suggerisco di aggiungere le tue mappature contenenti solo le estensioni di file rilevanti per te (come ho fatto sopra).

Nota:se desideri utilizzare FileExtensionContentTypeProvider, è nel pacchetto Microsoft.AspNetCore.StaticFiles .