Przesyłanie plików w ASP.net bez użycia kontrolki serwera FileUpload


99

Jak uzyskać formularz sieciowy ASP.net (v3.5), aby opublikować plik przy użyciu zwykłego starego <input type="file" />?

Nie jestem zainteresowany użyciem formantu serwera ASP.net FileUpload.

Odpowiedzi:


133

W twoim aspx:

<form id="form1" runat="server" enctype="multipart/form-data">
 <input type="file" id="myFile" name="myFile" />
 <asp:Button runat="server" ID="btnUpload" OnClick="btnUploadClick" Text="Upload" />
</form>

W kodzie za:

protected void btnUploadClick(object sender, EventArgs e)
{
    HttpPostedFile file = Request.Files["myFile"];

    //check file was submitted
    if (file != null && file.ContentLength > 0)
    {
        string fname = Path.GetFileName(file.FileName);
        file.SaveAs(Server.MapPath(Path.Combine("~/App_Data/", fname)));
    }
}

3
@mathieu, szkoda, że ​​ten wariant używa runat="server", nie jest niezależny od serwera ASP.NET
Secret

co, jeśli jest więcej niż 1 wejście i chcesz, aby oddzielne funkcje były wykonywane na obu zestawach plików.
Neville Nazerane

Używając go do przesyłania wielu plików, zwraca tę samą nazwę pliku dla wszystkich plików i dlatego zapisuje pierwszy plik n razy. Masz pomysł, jak się z tym uporać?
sohaiby

@Martina Sprawdź ten fragment kodu, aby zapisać wiele plików
sohaiby

1
@DanielJ. Nie ma możliwości, aby nie używać kodu związanego z plikiem, jeśli zamierzasz potem cokolwiek robić z plikiem, tj. Przechowywać go w bazie danych. Jasne, możesz użyć prostego JavaScript, aby pobrać dane wejściowe i plik: var fileUploaded = document.getElementById("myFile").files[0];a nawet możesz uzyskać bajty za pomocą readAsArrayBuffer: stackoverflow.com/questions/37134433/… - ale co zamierzasz zrobić z tymi wszystkimi bajtami po stronie klienta? OP chce całkowicie uniknąć komunikacji po stronie serwera, a nie sterowania ASP .NET. -1 do ciebie.
vapcguy

40

Oto rozwiązanie bez polegania na jakiejkolwiek kontroli po stronie serwera, tak jak OP opisał w pytaniu.

Kod HTML po stronie klienta:

<form action="upload.aspx" method="post" enctype="multipart/form-data">
    <input type="file" name="UploadedFile" />
</form>

Metoda Page_Load pliku upload.aspx:

if(Request.Files["UploadedFile"] != null)
{
    HttpPostedFile MyFile = Request.Files["UploadedFile"];
    //Setting location to upload files
    string TargetLocation = Server.MapPath("~/Files/");
    try
    {
        if (MyFile.ContentLength > 0)
        {
            //Determining file name. You can format it as you wish.
            string FileName = MyFile.FileName;
            //Determining file size.
            int FileSize = MyFile.ContentLength;
            //Creating a byte array corresponding to file size.
            byte[] FileByteArray = new byte[FileSize];
            //Posted file is being pushed into byte array.
            MyFile.InputStream.Read(FileByteArray, 0, FileSize);
            //Uploading properly formatted file to server.
            MyFile.SaveAs(TargetLocation + FileName);
        }
    }
    catch(Exception BlueScreen)
    {
        //Handle errors
    }
}

Uważaj na wywołanie HTTPWebRequest wysyłające pełną ścieżkę do pliku w MyFile.FileName. Wywołanie WebClient po prostu wysyła nazwę pliku. HTTPWebRequest spowoduje niepowodzenie operacji MyFile.SaveAs. Po prostu straciłem godziny na gonienie jednego klienta pracującego, a jednego nie.
Bob Clegg

Po prostu użyłem Request.Files[0]zamiast Request.Files["UploadedFile"]i podczas gdy OP używał prostego ASP .NET, a nie MVC, jeśli ktoś chce użyć tego dla MVC, po prostu potrzebujesz HttpPostedFileBasezamiast HttpPostedFile.
vapcguy

23

Będziesz musiał ustawić enctypeatrybut formto multipart/form-data; wtedy możesz uzyskać dostęp do przesłanego pliku za pomocą HttpRequest.Fileskolekcji.


Mój formularz serwera znajdował się na stronie wzorcowej i nie mogłem dodać multipart / form-data (od tego czasu byłby na każdej stronie). Szybkim i łatwym obejściem było dodanie do strony ukrytej kontrolki FileUpload. Następnie WebForms automatycznie dodały dane multipart / form. Musiałem to zrobić, ponieważ korzystałem z pliku wejściowego w Angular2 i nie mogłem użyć kontrolki serwera w szablonie komponentu.
Jason

9

użyj kontrolki HTML z atrybutem serwera runat

 <input id="FileInput" runat="server" type="file" />

Następnie w asp.net Codebehind

 FileInput.PostedFile.SaveAs("DestinationPath");

Istnieją również opcje imprezy trzeciej , które pokażą postęp, jeśli jesteś zainteresowany


3
To znowu zamienia go w kontrolę serwera. ;)
ahwm

@ahwm OP chciał uniknąć formantu FileUpload w ASP .NET ( <asp:FileUpload ID="fileUpload1" runat="server"></asp:FileUpload>). To coś innego niż powiedzenie, aby nie umieszczać a runat=serverna inputpolu.
vapcguy

1
Rozumiem, że nie chcą używać formantów ASP.NET. Który obejmuje HtmlInputFilekontrolę.
ahwm

8

Tak, możesz to osiągnąć metodą postu Ajax. po stronie serwera możesz użyć httphandler. Dlatego nie używamy żadnych elementów sterujących serwera zgodnie z Twoimi wymaganiami.

dzięki Ajax możesz również pokazać postęp przesyłania.

będziesz musiał czytać plik jako strumień wejściowy.

using (FileStream fs = File.Create("D:\\_Workarea\\" + fileName))
    {
        Byte[] buffer = new Byte[32 * 1024];
        int read = context.Request.GetBufferlessInputStream().Read(buffer, 0, buffer.Length);
        while (read > 0)
        {
            fs.Write(buffer, 0, read);
            read = context.Request.GetBufferlessInputStream().Read(buffer, 0, buffer.Length);
        }
    } 

Przykładowy kod

function sendFile(file) {              
        debugger;
        $.ajax({
            url: 'handler/FileUploader.ashx?FileName=' + file.name, //server script to process data
            type: 'POST',
            xhr: function () {
                myXhr = $.ajaxSettings.xhr();
                if (myXhr.upload) {
                    myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
                }
                return myXhr;
            },
            success: function (result) {                    
                //On success if you want to perform some tasks.
            },
            data: file,
            cache: false,
            contentType: false,
            processData: false
        });
        function progressHandlingFunction(e) {
            if (e.lengthComputable) {
                var s = parseInt((e.loaded / e.total) * 100);
                $("#progress" + currFile).text(s + "%");
                $("#progbarWidth" + currFile).width(s + "%");
                if (s == 100) {
                    triggerNextFileUpload();
                }
            }
        }
    }

2
Może powinieneś dodać wskazówkę, aby dodać fs.close()wszystko inne może skończyć się w nieczytelnych plikach, ponieważ pozostają one w jakiś sposób otwarte w usługach IIS.
mistapink

@mistapink Czy strumień pliku nie jest zamknięty, gdy wykonanie opuszcza using blok?
Sebi

@Sebi wygląda na to, że prawie 3 lata temu tak się nie stało. Nie próbowałem tego przez długi czas, więc nie mogę tutaj dać jednoznacznej odpowiedzi. Przepraszam.
mistapink

4

Kolekcja Request.Files zawiera wszystkie pliki przesłane wraz z formularzem, niezależnie od tego, czy pochodzą one z formantu FileUpload, czy z ręcznie zapisanego <input type="file">.

Możesz więc po prostu napisać zwykły stary znacznik wejściowy pliku w środku swojego WebForm, a następnie odczytać plik przesłany z kolekcji Request.Files.


4

Ponieważ inni odpowiedzieli, Request.Files to HttpFileCollection, który zawiera wszystkie opublikowane pliki, wystarczy poprosić ten obiekt o plik w następujący sposób:

Request.Files["myFile"]

Ale co się stanie, gdy istnieje więcej niż jeden znacznik wejściowy o tej samej nazwie atrybutu:

Select file 1 <input type="file" name="myFiles" />
Select file 2 <input type="file" name="myFiles" />

Po stronie serwera poprzedni kod Request.Files ["myFile"] zwraca tylko jeden obiekt HttpPostedFile zamiast dwóch plików. Widziałem w .net 4.5 metodę rozszerzającą o nazwie GetMultiple, ale dla poprzednich wersji nie istnieje, w tym celu proponuję metodę rozszerzenia jako:

public static IEnumerable<HttpPostedFile> GetMultiple(this HttpFileCollection pCollection, string pName)
{
        for (int i = 0; i < pCollection.Count; i++)
        {
            if (pCollection.GetKey(i).Equals(pName))
            {
                yield return pCollection.Get(i);
            }
        }
}

Ta metoda rozszerzenia zwróci wszystkie obiekty HttpPostedFile, które mają nazwę „myFiles” w HttpFileCollection, jeśli takie istnieją.


2

Kontrolka HtmlInputFile

Używałem tego cały czas.


2
Wymaga to dodania, runat="server"co zasadniczo zmienia go w kontrolkę serwera, której nie chcieli używać.
ahwm

@ahwm Nie, OP nie chciał używać określonej kontrolki FileUpload ASP .NET. Różni się to od stwierdzenia, że ​​ogólnie rzecz biorąc, nie chcą używać formantu po stronie serwera.
vapcguy


0
//create a folder in server (~/Uploads)
 //to upload
 File.Copy(@"D:\CORREO.txt", Server.MapPath("~/Uploads/CORREO.txt"));

 //to download
             Response.ContentType = ContentType;
             Response.AppendHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName("~/Uploads/CORREO.txt"));
             Response.WriteFile("~/Uploads/CORREO.txt");
             Response.End();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.