Ich versuche, flow.js (https://github.com/flowjs/flow.js) über seinen Angular Wrapper (https://github.com/flowjs/ng-flow/tree/master/samples/basic) zu verwenden, um Dateien auf einen ASP.NET WebAPI 2 Server hochzuladen. Wie auch immer, wenn ich eine Datei wähle, um meine WebAPI hochzuladen, bekomme ich nur die erste Chunk GET Anfrage und dann passiert nichts: kein POST ist gemacht, und es scheint, dass flow.js den Upload nicht gestartet hat.Upload von Dateien mit flow.js + ng-flow zu WebAPI 2
Die anfängliche GET ausgelöst, wenn ich eine Datei auswählen, ist:
GET http://localhost:49330/api/upload?flowChunkNumber=1&flowChunkSize=1048576&flowCurrentChunkSize=4751&flowTotalSize=4751&flowIdentifier=4751-ElmahMySqlsql&flowFilename=Elmah.MySql.sql&flowRelativePath=Elmah.MySql.sql&flowTotalChunks=1 HTTP/1.1
Host: localhost:49330
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36
Accept: */*
Referer: http://localhost:49330/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,it;q=0.6
Und die Antwort ist:
HTTP/1.1 202 Accepted
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcNDViXFRlc3RcVXBUZXN0XFVwVGVzdFxhcGlcdXBsb2Fk?=
X-Powered-By: ASP.NET
Date: Fri, 17 Apr 2015 08:02:56 GMT
Content-Length: 0
Dann nicht mehr Anfragen ausgegeben werden.
Wie es scheint, gibt es kein aktuelles WebAPI-Beispiel, sondern nur vereinzelte Posts, die ich für Neulinge wie mich eine Dummy-Repro-Lösung erstellt habe, die Sie von http://1drv.ms/1CSF5jq herunterladen können: es ist eine ASP.NET WebAPI 2-Lösung, wo ich platziert den Upload-Code in der Home-Ansicht, nachdem der entsprechende API-Controller hinzugefügt wurde. Drücken Sie einfach F5 und versuchen Sie, eine Datei hochzuladen. Sie finden den API-Controller unter UploadController.cs
.
Die entsprechenden Codeteile sind:
a) Client-Seite: eine Seite, ähnlich wie das Schnellstart Beispiel der ng-Flow Seite:
<div class="row">
<div class="col-md-12">
<div flow-init="{target: '/api/upload'}"
flow-files-submitted="$flow.upload()"
flow-file-success="$file.msg = $message">
<input type="file" flow-btn />
<ol>
<li ng-repeat="file in $flow.files">{{file.name}}: {{file.msg}}</li>
</ol>
</div>
</div>
</div>
der entsprechende Code ist im Wesentlichen ein empty TS Skelett mit dem Modul Initialisierung:
module Up {
export interface IMainScope {
}
export class MainController {
public static $inject = ["$scope"];
constructor(private $scope: IMainScope) {
}
}
var app = angular.module("app", ["flow"]);
app.controller("mainController", MainController);
}
b) Serverseite: Ich habe ein Bündel für die erforderlichen Skripts und den folgenden Controller hinzugefügt, der von dem Beispielcode geändert wurde, den ich unter How to upload file in chunks in ASP.NET using ng-Flow gefunden habe. Beachten Sie, dass ich in der GET Upload
Methode die Signatur mithilfe eines Bindungsmodells änderte (andernfalls erhielten wir 404, da die Route nicht übereinstimmte), und wenn der Chunk nicht gefunden wurde, gab ich einen 202 - Accepted
Code statt 404 als flow.js zurück Die Dokumentation besagt, dass 200 entspricht "Der Chunk wurde akzeptiert und korrekt. Kein Neustart erforderlich", während ein 404 den gesamten Upload abbricht und jeder andere Code (wie hier 202) den Uploader anweist, es erneut zu versuchen.
[RoutePrefix("api")]
public class UploadController : ApiController
{
private readonly string _sRoot;
public UploadController()
{
_sRoot = HostingEnvironment.MapPath("~/App_Data/Uploads");
}
[Route("upload"), AcceptVerbs("GET")]
public IHttpActionResult Upload([FromUri] UploadBindingModel model)
{
if (IsChunkHere(model.FlowChunkNumber, model.FlowIdentifier)) return Ok();
return ResponseMessage(new HttpResponseMessage(HttpStatusCode.Accepted));
}
[Route("upload"), AcceptVerbs("POST")]
public async Task<IHttpActionResult> Upload()
{
// ensure that the request contains multipart/form-data
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
if (!Directory.Exists(_sRoot)) Directory.CreateDirectory(_sRoot);
MultipartFormDataStreamProvider provider =
new MultipartFormDataStreamProvider(_sRoot);
try
{
await Request.Content.ReadAsMultipartAsync(provider);
int nChunkNumber = Convert.ToInt32(provider.FormData["flowChunkNumber"]);
int nTotalChunks = Convert.ToInt32(provider.FormData["flowTotalChunks"]);
string sIdentifier = provider.FormData["flowIdentifier"];
string sFileName = provider.FormData["flowFilename"];
// rename the generated file
MultipartFileData chunk = provider.FileData[0]; // Only one file in multipart message
RenameChunk(chunk, nChunkNumber, sIdentifier);
// assemble chunks into single file if they're all here
TryAssembleFile(sIdentifier, nTotalChunks, sFileName);
return Ok();
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
private string GetChunkFileName(int chunkNumber, string identifier)
{
return Path.Combine(_sRoot,
String.Format(CultureInfo.InvariantCulture, "{0}_{1}",
identifier, chunkNumber));
}
private void RenameChunk(MultipartFileData chunk, int chunkNumber, string identifier)
{
string sGeneratedFileName = chunk.LocalFileName;
string sChunkFileName = GetChunkFileName(chunkNumber, identifier);
if (File.Exists(sChunkFileName)) File.Delete(sChunkFileName);
File.Move(sGeneratedFileName, sChunkFileName);
}
private string GetFileName(string identifier)
{
return Path.Combine(_sRoot, identifier);
}
private bool IsChunkHere(int chunkNumber, string identifier)
{
string sFileName = GetChunkFileName(chunkNumber, identifier);
return File.Exists(sFileName);
}
private bool AreAllChunksHere(string identifier, int totalChunks)
{
for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++)
if (!IsChunkHere(nChunkNumber, identifier)) return false;
return true;
}
private void TryAssembleFile(string identifier, int totalChunks, string filename)
{
if (!AreAllChunksHere(identifier, totalChunks)) return;
// create a single file
string sConsolidatedFileName = GetFileName(identifier);
using (Stream destStream = File.Create(sConsolidatedFileName, 15000))
{
for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++)
{
string sChunkFileName = GetChunkFileName(nChunkNumber, identifier);
using (Stream sourceStream = File.OpenRead(sChunkFileName))
{
sourceStream.CopyTo(destStream);
}
} //efor
destStream.Close();
}
// rename consolidated with original name of upload
// strip to filename if directory is specified (avoid cross-directory attack)
filename = Path.GetFileName(filename);
Debug.Assert(filename != null);
string sRealFileName = Path.Combine(_sRoot, filename);
if (File.Exists(filename)) File.Delete(sRealFileName);
File.Move(sConsolidatedFileName, sRealFileName);
// delete chunk files
for (int nChunkNumber = 1; nChunkNumber <= totalChunks; nChunkNumber++)
{
string sChunkFileName = GetChunkFileName(nChunkNumber, identifier);
File.Delete(sChunkFileName);
} //efor
}
}
Ich muss hinzufügen, dass nach https://github.com/flowjs/ng-flow/issues/144 und scheinbar im Gegensatz zu der Dokumentation, es scheint 404 sollte von GET zurückgegeben werden, wenn der Chunk nicht gefunden wird. Ich habe das versucht, aber nichts ändert sich und kein Upload beginnt. – Naftis