Ich verwende asp.net mvc 4 webapi beta, um einen Ruhedienst zu erstellen. Ich muss in der Lage sein, gepostete Bilder/Dateien von Client-Anwendungen zu akzeptieren. Ist das mit dem Webapi möglich? Im Folgenden wird beschrieben, welche Aktion ich derzeit verwende. Kennt jemand ein Beispiel, wie das funktionieren soll?
[HttpPost]
public string ProfileImagePost(HttpPostedFile profileImage)
{
string[] extensions = { ".jpg", ".jpeg", ".gif", ".bmp", ".png" };
if (!extensions.Any(x => x.Equals(Path.GetExtension(profileImage.FileName.ToLower()), StringComparison.OrdinalIgnoreCase)))
{
throw new HttpResponseException("Invalid file type.", HttpStatusCode.BadRequest);
}
// Other code goes here
return "/path/to/image.png";
}
siehe http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmime , obwohl ich denke, der Artikel macht es so etwas komplizierter als es wirklich ist.
Grundsätzlich gilt,
public Task<HttpResponseMessage> PostFile()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.BodyPartFileNames.First().Value;
// this is the file name on the server where the file was saved
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
Ich bin überrascht, dass viele von Ihnen Dateien auf dem Server speichern möchten. Die Lösung, um alles im Speicher zu behalten, lautet wie folgt:
[HttpPost, Route("api/upload")]
public async Task<IHttpActionResult> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
//Do whatever you want with filename and its binary data.
}
return Ok();
}
Siehe den nachstehenden Code, angepasst an diesen Artikel , der den einfachsten Beispielcode zeigt, den ich finden konnte. Es enthält sowohl Datei- als auch memory (schnellere) Uploads.
public HttpResponseMessage Post()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count < 1)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
foreach(string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
postedFile.SaveAs(filePath);
// NOTE: To store in memory use postedFile.InputStream
}
return Request.CreateResponse(HttpStatusCode.Created);
}
Ich habe die Antwort von Mike Wasson verwendet, bevor ich alle NuGets in meinem webapi mvc4-Projekt aktualisiert habe. Danach musste ich die Datei-Upload-Aktion erneut schreiben:
public Task<HttpResponseMessage> Upload(int id)
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);
string guid = Guid.NewGuid().ToString();
File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
Offensichtlich ist BodyPartFileNames nicht mehr im MultipartFormDataStreamProvider verfügbar.
Hier ist eine schnelle und schmutzige Lösung, die hochgeladene Dateiinhalte aus dem HTTP-Body übernimmt und in eine Datei schreibt. Ich habe ein HTML/JS-Snippet "Bare Bones" für den Dateiupload beigelegt.
Web-API-Methode:
[Route("api/myfileupload")]
[HttpPost]
public string MyFileUpload()
{
var request = HttpContext.Current.Request;
var filePath = "C:\\temp\\" + request.Headers["filename"];
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
{
request.InputStream.CopyTo(fs);
}
return "uploaded";
}
HTML-Datei hochladen:
<form>
<input type="file" id="myfile"/>
<input type="button" onclick="uploadFile();" value="Upload" />
</form>
<script type="text/javascript">
function uploadFile() {
var xhr = new XMLHttpRequest();
var file = document.getElementById('myfile').files[0];
xhr.open("POST", "api/myfileupload");
xhr.setRequestHeader("filename", file.name);
xhr.send(file);
}
</script>
Der ASP.NET Core-Weg ist jetzt hier :
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
// full path to file in temp location
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
// process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size, filePath});
}
In die gleiche Richtung schreibe ich ein Client- und ein Server-Snipet, die Excel-Dateien mit WebApi senden. C # 4:
public static void SetFile(String serviceUrl, byte[] fileArray, String fileName)
{
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var content = new MultipartFormDataContent())
{
var fileContent = new ByteArrayContent(fileArray);//(System.IO.File.ReadAllBytes(fileName));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
content.Add(fileContent);
var result = client.PostAsync(serviceUrl, content).Result;
}
}
}
catch (Exception e)
{
//Log the exception
}
}
Und der Server-Webapi-Controller:
public Task<IEnumerable<string>> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
return "File uploaded as " + info.FullName + " (" + info.Length + ")";
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
}
}
Und der benutzerdefinierte MyMultipartFormDataStreamProvider, der zum Anpassen des Dateinamens benötigt wird:
PS: Ich habe diesen Code einem anderen Beitrag entnommen http://www.codeguru.com/csharp/.net/uploading-files-asynchronously-using-asp.net-web-api.htm)
public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public MyMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
string fileName;
if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
{
fileName = headers.ContentDisposition.FileName;
}
else
{
fileName = Guid.NewGuid().ToString() + ".data";
}
return fileName.Replace("\"", string.Empty);
}
}
[HttpPost]
public JsonResult PostImage(HttpPostedFileBase file)
{
try
{
if (file != null && file.ContentLength > 0 && file.ContentLength<=10485760)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/") + "HisloImages" + "\\", fileName);
file.SaveAs(path);
#region MyRegion
////save imag in Db
//using (MemoryStream ms = new MemoryStream())
//{
// file.InputStream.CopyTo(ms);
// byte[] array = ms.GetBuffer();
//}
#endregion
return Json(JsonResponseFactory.SuccessResponse("Status:0 ,Message: OK"), JsonRequestBehavior.AllowGet);
}
else
{
return Json(JsonResponseFactory.ErrorResponse("Status:1 , Message: Upload Again and File Size Should be Less Than 10MB"), JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(JsonResponseFactory.ErrorResponse(ex.Message), JsonRequestBehavior.AllowGet);
}
}
Es gibt zwei Möglichkeiten, eine Datei zu akzeptieren. Eine Verwendung im Speicheranbieter MultipartMemoryStreamProvider und eine Verwendung von MultipartFormDataStreamProvider , die auf einer Festplatte gespeichert werden. Beachten Sie, dass dies jeweils nur für einen Dateiupload gilt. Sie können dies sicher erweitern, um mehrere Dateien zu speichern. Der zweite Ansatz kann große Dateien unterstützen. Ich habe Dateien über 200 MB getestet und es funktioniert gut. Bei der Verwendung des Arbeitsspeichers müssen Sie nicht auf der Festplatte speichern. Wenn Sie jedoch einen bestimmten Grenzwert überschreiten, wird eine Ausnahme aus dem Arbeitsspeicher ausgelöst.
private async Task<Stream> ReadStream()
{
Stream stream = null;
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var buffer = await file.ReadAsByteArrayAsync();
stream = new MemoryStream(buffer);
}
return stream;
}
private async Task<Stream> ReadLargeStream()
{
Stream stream = null;
string root = Path.GetTempPath();
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.FileData)
{
var path = file.LocalFileName;
byte[] content = File.ReadAllBytes(path);
File.Delete(path);
stream = new MemoryStream(content);
}
return stream;
}
Diese Frage hat auch für .Net Core viele gute Antworten. Ich verwendete beide Frameworks. Die bereitgestellten Codebeispiele funktionieren einwandfrei. Also werde ich es nicht wiederholen. In meinem Fall war es wichtig, wie Sie Datei-Upload-Aktionen mit Swagger wie folgt verwenden:
Hier ist meine Zusammenfassung:
ASP .Net WebAPI 2
.NET Core
Ich hatte ein ähnliches Problem für die Vorschau-Web-API. Portierte diesen Teil noch nicht auf die neue MVC 4-Web-API, aber vielleicht hilft dies:
REST Datei mit HttpRequestMessage oder Stream hochladen?
Bitte lass es mich wissen, kann mich morgen hinsetzen und versuchen, es erneut zu implementieren.
API-Controller:
[HttpPost]
public HttpResponseMessage Post()
{
var httpRequest = System.Web.HttpContext.Current.Request;
if (System.Web.HttpContext.Current.Request.Files.Count < 1)
{
//TODO
}
else
{
try
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
BinaryReader binReader = new BinaryReader(postedFile.InputStream);
byte[] byteArray = binReader.ReadBytes(postedFile.ContentLength);
}
}
catch (System.Exception e)
{
//TODO
}
}
return Request.CreateResponse(HttpStatusCode.Created);
}