Как загрузить файл на сервер с помощью HTTP POST multipart / form-data
Я разрабатываю приложение для Windows Phone 8. Я хочу загрузить базу данных SQLite через веб-службу PHP с помощью HTTP-запроса POST с типом MIME multipart / form-data и строковыми данными под названием "userid=SOME_ID". Я не хочу использовать сторонние библиотеки, такие как HttpClient, RestSharp или MyToolkit. Я попробовал ниже код, но он не загружает файл и также не дает мне никаких ошибок. Он отлично работает в Android, PHP и т. д., Поэтому в веб-службе нет проблем. Ниже приведен мой код (для WP8). что плохого это?
я погуглил свою проблему, но ничего не работает для меня. Я не получаю конкретных для WP8
async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(DBNAME);
//Below line gives me file with 0 bytes, why? Should I use
//IsolatedStorageFile instead of StorageFile
//var file = await ApplicationData.Current.LocalFolder.GetFileAsync(DBNAME);
byte[] fileBytes = null;
using (var stream = await file.OpenReadAsync())
{
fileBytes = new byte[stream.Size];
using (var reader = new DataReader(stream))
{
await reader.LoadAsync((uint)stream.Size);
reader.ReadBytes(fileBytes);
}
}
//var res = await HttpPost(Util.UPLOAD_BACKUP, fileBytes);
HttpPost(fileBytes);
}
private void HttpPost(byte[] file_bytes)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create("http://www.myserver.com/upload.php");
httpWebRequest.ContentType = "multipart/form-data";
httpWebRequest.Method = "POST";
var asyncResult = httpWebRequest.BeginGetRequestStream((ar) => { GetRequestStreamCallback(ar, file_bytes); }, httpWebRequest);
}
private void GetRequestStreamCallback(IAsyncResult asynchronousResult, byte[] postData)
{
//DON'T KNOW HOW TO PASS "userid=some_user_id"
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
Stream postStream = request.EndGetRequestStream(asynchronousResult);
postStream.Write(postData, 0, postData.Length);
postStream.Close();
var asyncResult = request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
streamResponse.Close();
streamRead.Close();
response.Close();
}
Я также пытался решить мою проблему в Windows 8, но это также не работает.
public async Task Upload(byte[] fileBytes)
{
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(System.Globalization.CultureInfo.InvariantCulture)))
{
content.Add(new StreamContent(new MemoryStream(fileBytes)));
//Not sure below line is true or not
content.Add(new StringContent("userid=farhanW8"));
using (var message = await client.PostAsync("http://www.myserver.com/upload.php", content))
{
var input = await message.Content.ReadAsStringAsync();
}
}
}
}
8 ответов:
Я сделал это с помощью MultipartFormDataContent : -
HttpClient httpClient = new HttpClient(); MultipartFormDataContent form = new MultipartFormDataContent(); form.Add(new StringContent(username), "username"); form.Add(new StringContent(useremail), "email"); form.Add(new StringContent(password), "password"); form.Add(new ByteArrayContent(file_bytes, 0, file_bytes.Length), "profile_pic", "hello1.jpg"); HttpResponseMessage response = await httpClient.PostAsync("PostUrl", form); response.EnsureSuccessStatusCode(); httpClient.Dispose(); string sd = response.Content.ReadAsStringAsync().Result;
вот мой последний рабочий код. Мой веб-сервиса нужен один файл (название поста параметр "файл") и строковое значение (наименование параметра пост был "идентификатор пользователя").
/// <summary> /// Occurs when upload backup application bar button is clicked. Author : Farhan Ghumra /// </summary> private async void btnUploadBackup_Click(object sender, EventArgs e) { var dbFile = await ApplicationData.Current.LocalFolder.GetFileAsync(Util.DBNAME); var fileBytes = await GetBytesAsync(dbFile); var Params = new Dictionary<string, string> { { "userid", "9" } }; UploadFilesToServer(new Uri(Util.UPLOAD_BACKUP), Params, Path.GetFileName(dbFile.Path), "application/octet-stream", fileBytes); } /// <summary> /// Creates HTTP POST request & uploads database to server. Author : Farhan Ghumra /// </summary> private void UploadFilesToServer(Uri uri, Dictionary<string, string> data, string fileName, string fileContentType, byte[] fileData) { string boundary = "----------" + DateTime.Now.Ticks.ToString("x"); HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri); httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary; httpWebRequest.Method = "POST"; httpWebRequest.BeginGetRequestStream((result) => { try { HttpWebRequest request = (HttpWebRequest)result.AsyncState; using (Stream requestStream = request.EndGetRequestStream(result)) { WriteMultipartForm(requestStream, boundary, data, fileName, fileContentType, fileData); } request.BeginGetResponse(a => { try { var response = request.EndGetResponse(a); var responseStream = response.GetResponseStream(); using (var sr = new StreamReader(responseStream)) { using (StreamReader streamReader = new StreamReader(response.GetResponseStream())) { string responseString = streamReader.ReadToEnd(); //responseString is depend upon your web service. if (responseString == "Success") { MessageBox.Show("Backup stored successfully on server."); } else { MessageBox.Show("Error occurred while uploading backup on server."); } } } } catch (Exception) { } }, null); } catch (Exception) { } }, httpWebRequest); } /// <summary> /// Writes multi part HTTP POST request. Author : Farhan Ghumra /// </summary> private void WriteMultipartForm(Stream s, string boundary, Dictionary<string, string> data, string fileName, string fileContentType, byte[] fileData) { /// The first boundary byte[] boundarybytes = Encoding.UTF8.GetBytes("--" + boundary + "\r\n"); /// the last boundary. byte[] trailer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); /// the form data, properly formatted string formdataTemplate = "Content-Dis-data; name=\"{0}\"\r\n\r\n{1}"; /// the form-data file upload, properly formatted string fileheaderTemplate = "Content-Dis-data; name=\"{0}\"; filename=\"{1}\";\r\nContent-Type: {2}\r\n\r\n"; /// Added to track if we need a CRLF or not. bool bNeedsCRLF = false; if (data != null) { foreach (string key in data.Keys) { /// if we need to drop a CRLF, do that. if (bNeedsCRLF) WriteToStream(s, "\r\n"); /// Write the boundary. WriteToStream(s, boundarybytes); /// Write the key. WriteToStream(s, string.Format(formdataTemplate, key, data[key])); bNeedsCRLF = true; } } /// If we don't have keys, we don't need a crlf. if (bNeedsCRLF) WriteToStream(s, "\r\n"); WriteToStream(s, boundarybytes); WriteToStream(s, string.Format(fileheaderTemplate, "file", fileName, fileContentType)); /// Write the file data to the stream. WriteToStream(s, fileData); WriteToStream(s, trailer); } /// <summary> /// Writes string to stream. Author : Farhan Ghumra /// </summary> private void WriteToStream(Stream s, string txt) { byte[] bytes = Encoding.UTF8.GetBytes(txt); s.Write(bytes, 0, bytes.Length); } /// <summary> /// Writes byte array to stream. Author : Farhan Ghumra /// </summary> private void WriteToStream(Stream s, byte[] bytes) { s.Write(bytes, 0, bytes.Length); } /// <summary> /// Returns byte array from StorageFile. Author : Farhan Ghumra /// </summary> private async Task<byte[]> GetBytesAsync(StorageFile file) { byte[] fileBytes = null; using (var stream = await file.OpenReadAsync()) { fileBytes = new byte[stream.Size]; using (var reader = new DataReader(stream)) { await reader.LoadAsync((uint)stream.Size); reader.ReadBytes(fileBytes); } } return fileBytes; }
Я очень благодарен Дарин Руссо помогаешь мне.
эта упрощенная версия также работает.
public void UploadMultipart(byte[] file, string filename, string contentType, string url) { var webClient = new WebClient(); string boundary = "------------------------" + DateTime.Now.Ticks.ToString("x"); webClient.Headers.Add("Content-Type", "multipart/form-data; boundary=" + boundary); var fileData = webClient.Encoding.GetString(file); var package = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n{3}\r\n--{0}--\r\n", boundary, filename, contentType, fileData); var nfile = webClient.Encoding.GetBytes(package); byte[] resp = webClient.UploadData(url, "POST", nfile); }
добавить любые дополнительные необходимые заголовки, если это необходимо.
Я немного поиграл и придумал упрощенное, более общее решение:
private static string sendHttpRequest(string url, NameValueCollection values, NameValueCollection files = null) { string boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x"); // The first boundary byte[] boundaryBytes = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n"); // The last boundary byte[] trailer = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); // The first time it itereates, we need to make sure it doesn't put too many new paragraphs down or it completely messes up poor webbrick byte[] boundaryBytesF = System.Text.Encoding.ASCII.GetBytes("--" + boundary + "\r\n"); // Create the request and set parameters HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url); request.ContentType = "multipart/form-data; boundary=" + boundary; request.Method = "POST"; request.KeepAlive = true; request.Credentials = System.Net.CredentialCache.DefaultCredentials; // Get request stream Stream requestStream = request.GetRequestStream(); foreach (string key in values.Keys) { // Write item to stream byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}", key, values[key])); requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); requestStream.Write(formItemBytes, 0, formItemBytes.Length); } if (files != null) { foreach(string key in files.Keys) { if(File.Exists(files[key])) { int bytesRead = 0; byte[] buffer = new byte[2048]; byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n", key, files[key])); requestStream.Write(boundaryBytes, 0, boundaryBytes.Length); requestStream.Write(formItemBytes, 0, formItemBytes.Length); using (FileStream fileStream = new FileStream(files[key], FileMode.Open, FileAccess.Read)) { while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { // Write file content to stream, byte by byte requestStream.Write(buffer, 0, bytesRead); } fileStream.Close(); } } } } // Write trailer and close stream requestStream.Write(trailer, 0, trailer.Length); requestStream.Close(); using (StreamReader reader = new StreamReader(request.GetResponse().GetResponseStream())) { return reader.ReadToEnd(); }; }
вы можете использовать его как это:
string fileLocation = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + Path.DirectorySeparatorChar + "somefile.jpg"; NameValueCollection values = new NameValueCollection(); NameValueCollection files = new NameValueCollection(); values.Add("firstName", "Alan"); files.Add("profilePicture", fileLocation); sendHttpRequest("http://example.com/handler.php", values, files);
и в PHP-скрипте вы можете обрабатывать такие данные:
echo $_POST['firstName']; $name = $_POST['firstName']; $image = $_FILES['profilePicture']; $ds = DIRECTORY_SEPARATOR; move_uploaded_file($image['tmp_name'], realpath(dirname(__FILE__)) . $ds . "uploads" . $ds . $image['name']);
вы можете использовать этот класс:
using System.Collections.Specialized; class Post_File { public static void HttpUploadFile(string url, string file, string paramName, string contentType, NameValueCollection nvc) { string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x"); byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); byte[] boundarybytesF = System.Text.Encoding.ASCII.GetBytes("--" + boundary + "\r\n"); // the first time it itereates, you need to make sure it doesn't put too many new paragraphs down or it completely messes up poor webbrick. HttpWebRequest wr = (HttpWebRequest)WebRequest.Create(url); wr.Method = "POST"; wr.KeepAlive = true; wr.Credentials = System.Net.CredentialCache.DefaultCredentials; wr.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"; var nvc2 = new NameValueCollection(); nvc2.Add("Accepts-Language", "en-us,en;q=0.5"); wr.Headers.Add(nvc2); wr.ContentType = "multipart/form-data; boundary=" + boundary; Stream rs = wr.GetRequestStream(); bool firstLoop = true; string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; foreach (string key in nvc.Keys) { if (firstLoop) { rs.Write(boundarybytesF, 0, boundarybytesF.Length); firstLoop = false; } else { rs.Write(boundarybytes, 0, boundarybytes.Length); } string formitem = string.Format(formdataTemplate, key, nvc[key]); byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem); rs.Write(formitembytes, 0, formitembytes.Length); } rs.Write(boundarybytes, 0, boundarybytes.Length); string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n"; string header = string.Format(headerTemplate, paramName, new FileInfo(file).Name, contentType); byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header); rs.Write(headerbytes, 0, headerbytes.Length); FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read); byte[] buffer = new byte[4096]; int bytesRead = 0; while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { rs.Write(buffer, 0, bytesRead); } fileStream.Close(); byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); rs.Write(trailer, 0, trailer.Length); rs.Close(); WebResponse wresp = null; try { wresp = wr.GetResponse(); Stream stream2 = wresp.GetResponseStream(); StreamReader reader2 = new StreamReader(stream2); } catch (Exception ex) { if (wresp != null) { wresp.Close(); wresp = null; } } finally { wr = null; } } }
использовать:
NameValueCollection nvc = new NameValueCollection(); //nvc.Add("id", "TTR"); nvc.Add("table_name", "uploadfile"); nvc.Add("commit", "uploadfile"); Post_File.HttpUploadFile("http://example/upload_file.php", @"C:\user\yourfile.docx", "uploadfile", "application/vnd.ms-excel", nvc);
пример сервера
upload_file.php
:m('File upload '.(@copy($_FILES['uploadfile']['tmp_name'],getcwd().'\'.'/'.$_FILES['uploadfile']['name']) ? 'success' : 'failed')); function m($msg) { echo '<div style="background:#f1f1f1;border:1px solid #ddd;padding:15px;font:14px;text-align:center;font-weight:bold;">'; echo $msg; echo '</div>'; }
приведенный ниже код считывает файл, преобразует его в массив байтов, а затем делает запрос к серверу.
public void PostImage() { HttpClient httpClient = new HttpClient(); MultipartFormDataContent form = new MultipartFormDataContent(); byte[] imagebytearraystring = ImageFileToByteArray(@"C:\Users\Downloads\icon.png"); form.Add(new ByteArrayContent(imagebytearraystring, 0, imagebytearraystring.Count()), "profile_pic", "hello1.jpg"); HttpResponseMessage response = httpClient.PostAsync("your url", form).Result; httpClient.Dispose(); string sd = response.Content.ReadAsStringAsync().Result; } private byte[] ImageFileToByteArray(string fullFilePath) { FileStream fs = File.OpenRead(fullFilePath); byte[] bytes = new byte[fs.Length]; fs.Read(bytes, 0, Convert.ToInt32(fs.Length)); fs.Close(); return bytes; }
он работает для Windows phone 8.1. Вы можете попробовать это.
Dictionary<string, object> _headerContents = new Dictionary<string, object>(); const String _lineEnd = "\r\n"; const String _twoHyphens = "--"; const String _boundary = "*****"; private async void UploadFile_OnTap(object sender, System.Windows.Input.GestureEventArgs e) { Uri serverUri = new Uri("http:www.myserver.com/Mp4UploadHandler", UriKind.Absolute); string fileContentType = "multipart/form-data"; byte[] _boundarybytes = Encoding.UTF8.GetBytes(_twoHyphens + _boundary + _lineEnd); byte[] _trailerbytes = Encoding.UTF8.GetBytes(_twoHyphens + _boundary + _twoHyphens + _lineEnd); Dictionary<string, object> _headerContents = new Dictionary<string, object>(); SetEndHeaders(); // to add some extra parameter if you need httpWebRequest = (HttpWebRequest)WebRequest.Create(serverUri); httpWebRequest.ContentType = fileContentType + "; boundary=" + _boundary; httpWebRequest.Method = "POST"; httpWebRequest.AllowWriteStreamBuffering = false; // get response after upload header part var fileName = Path.GetFileName(MediaStorageFile.Path); Stream fStream = (await MediaStorageFile.OpenAsync(Windows.Storage.FileAccessMode.Read)).AsStream(); //MediaStorageFile is a storage file from where you want to upload the file of your device string fileheaderTemplate = "Content-Disposition: form-data; name=\"{0}\"" + _lineEnd + _lineEnd + "{1}" + _lineEnd; long httpLength = 0; foreach (var headerContent in _headerContents) // get the length of upload strem httpLength += _boundarybytes.Length + Encoding.UTF8.GetBytes(string.Format(fileheaderTemplate, headerContent.Key, headerContent.Value)).Length; httpLength += _boundarybytes.Length + Encoding.UTF8.GetBytes("Content-Disposition: form-data; name=\"uploadedFile\";filename=\"" + fileName + "\"" + _lineEnd).Length + Encoding.UTF8.GetBytes(_lineEnd).Length * 2 + _trailerbytes.Length; httpWebRequest.ContentLength = httpLength + fStream.Length; // wait until you upload your total stream httpWebRequest.BeginGetRequestStream((result) => { try { HttpWebRequest request = (HttpWebRequest)result.AsyncState; using (Stream stream = request.EndGetRequestStream(result)) { foreach (var headerContent in _headerContents) { WriteToStream(stream, _boundarybytes); WriteToStream(stream, string.Format(fileheaderTemplate, headerContent.Key, headerContent.Value)); } WriteToStream(stream, _boundarybytes); WriteToStream(stream, "Content-Disposition: form-data; name=\"uploadedFile\";filename=\"" + fileName + "\"" + _lineEnd); WriteToStream(stream, _lineEnd); int bytesRead = 0; byte[] buffer = new byte[2048]; //upload 2K each time while ((bytesRead = fStream.Read(buffer, 0, buffer.Length)) != 0) { stream.Write(buffer, 0, bytesRead); Array.Clear(buffer, 0, 2048); // Clear the array. } WriteToStream(stream, _lineEnd); WriteToStream(stream, _trailerbytes); fStream.Close(); } request.BeginGetResponse(a => { //get response here try { var response = request.EndGetResponse(a); using (Stream streamResponse = response.GetResponseStream()) using (var memoryStream = new MemoryStream()) { streamResponse.CopyTo(memoryStream); responseBytes = memoryStream.ToArray(); // here I get byte response from server. you can change depends on server response } if (responseBytes.Length > 0 && responseBytes[0] == 1) MessageBox.Show("Uploading Completed"); else MessageBox.Show("Uploading failed, please try again."); } catch (Exception ex) {} }, null); } catch (Exception ex) { fStream.Close(); } }, httpWebRequest); } private static void WriteToStream(Stream s, string txt) { byte[] bytes = Encoding.UTF8.GetBytes(txt); s.Write(bytes, 0, bytes.Length); } private static void WriteToStream(Stream s, byte[] bytes) { s.Write(bytes, 0, bytes.Length); } private void SetEndHeaders() { _headerContents.Add("sId", LocalData.currentUser.SessionId); _headerContents.Add("uId", LocalData.currentUser.UserIdentity); _headerContents.Add("authServer", LocalData.currentUser.AuthServerIP); _headerContents.Add("comPort", LocalData.currentUser.ComPort); }
для людей, ищущих 403 запрещенный вопрос при попытке загрузить в составном виде ниже может помочь, как есть случай в зависимости от конфигурации сервера, что вы получите MULTIPART_STRICT_ERROR"!@eq 0 " из-за неправильных заголовков MultipartFormDataContent. Обратите внимание, что обе переменные imagetag/filename включают цитаты (\") например filename="\ " myfile.формат PNG. ""\
MultipartFormDataContent form = new MultipartFormDataContent(); ByteArrayContent imageContent = new ByteArrayContent(fileBytes, 0, fileBytes.Length); imageContent.Headers.TryAddWithoutValidation("Content-Disposition", "form-data; name="+imagetag+"; filename="+filename); imageContent.Headers.TryAddWithoutValidation("Content-Type", "image / png"); form.Add(imageContent, imagetag, filename);