Próbuję użyć flow.js (https://github.com/flowjs/flow.js) za pomocą opakowania Angular (https://github.com/flowjs/ng-flow/tree/master/samples/basic) w celu przesłania plików na serwer ASP.NET WebAPI 2. W każdym razie, kiedy wybieram plik do przesłania, mój WebAPI właśnie pobiera pierwsze żądanie GET, a potem nic się nie dzieje: nie jest wykonywany POST i wygląda na to, że plik flow.js nie rozpoczął wysyłania.Przesyłanie plików za pomocą flow.js + ng-flow do WebAPI 2
Początkowy się zwolniony, kiedy wybrać plik jest:
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
A odpowiedź brzmi:
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
Wtedy nie więcej żądań są wydawane.
Wygląda na to, że nie ma aktualnego przykładu WebAPI, ale tylko rozproszone posty, które stworzyłem dla początkujących, takich jak ja, atypowego rozwiązania repro, które można pobrać z http://1drv.ms/1CSF5jq: jest to rozwiązanie ASP.NET WebAPI 2, w którym umieściłem kod do przesłania w widoku głównym, po dodaniu odpowiedniego kontrolera API. Po prostu naciśnij F5 i spróbuj załadować plik. Możesz znaleźć kontroler API w UploadController.cs
.
Odpowiednie elementy kodu są:
a) stronie klienta: strona podobny do szybkiego rozpoczęcia przykładzie strony ng przepływu:
<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>
odpowiedni kod jest zasadniczo puste TS szkielet z inicjalizacji module
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) strony serwera: Dodałem kilka wiązań dla wymaganych skryptów i następującego kontrolera, zmodyfikowanego na podstawie przykładowego kodu znalezionego pod adresem How to upload file in chunks in ASP.NET using ng-Flow. Zauważ, że w metodzie GET Upload
zmieniłem sygnaturę przy użyciu modelu wiążącego (w przeciwnym razie uzyskalibyśmy 404, ponieważ trasa nie została dopasowana), a gdy fragment nie zostanie znaleziony, zwracam kod 202 - Accepted
zamiast 404, jako flow.js dokumentacja mówi, że 200 odpowiada "Fragment został zaakceptowany i poprawny, nie trzeba ponownie przesyłać", podczas gdy 404 anuluje cały ładowanie, a każdy inny kod (na przykład 202 tutaj) informuje przesyłającego, aby spróbował ponownie.
[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
}
}
Muszę dodać, że zgodnie z https://github.com/flowjs/ng-flow/issues/144 i pozornie w przeciwieństwie do dokumentacji, wydaje się, że 404 powinno zostać zwrócone z GET, gdy porcja nie zostanie znaleziona. Próbowałem tego, ale nic się nie zmienia i nie rozpoczyna się wysyłanie. – Naftis