Przesyłanie dużego pliku do WCF z asp.net mvc 3

Przesyłanie dużego pliku do WCF z asp.net mvc 3
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:18 dni
0

Cześć. Mam problem z przesłaniem dużego pliku do WCFa. Pogrzebałem dużo w Internecie, niestety nie znalazłem rozwiązania swojego problemu.
Sprawa wygląda tak: mam usługę WCF hostowaną na IIS 6.1, której zadaniem jest odebranie pliku. Mam również stronkę w asp.net mvc 3 (hostowaną na tym samym serwerze) z prostym formularzem umożliwiającym wybranie i wysłanie pliku. Całość działa na Windowsie 7 Enterprise, piszę w Visual Studio 2010 Pro.
Wszystko działa, jeżeli w kontrolerze przyjmuję żądanie i wywołuję usługę WCF w jednym wątku, jeżeli spróbuję to zrobić w dwóch, to dostaję ObjectDisposedException z treścią "Cannot access a closed file.". Ponadto problem występuje tylko dla plików większych niż 80kB, niestety nie mam pojęcia, gdzie mam błąd w konfiguracji. Będę wdzięczny za pomoc.
Poniżej kody źródłowe:
WCF:

Kopiuj
 using System;
using System.ServiceModel;
using System.ServiceModel.Web;

namespace TransferManager
{
    [ServiceContract]
    public interface ITransferService
    {
        [OperationContract(Action = "UploadFile", IsOneWay = false)]
        void UploadFile(RemoteFileInfo request);
    }

    [MessageContract]
    public class RemoteFileInfo : IDisposable
    {
        [MessageHeader(MustUnderstand = true)]
        public string FileName;

        [MessageHeader(MustUnderstand = true)]
        public long Length;

        [MessageBodyMember(Order = 1)]
        public System.IO.Stream FileByteStream;

        public void Dispose()
        {
            if (FileByteStream != null)
            {
                FileByteStream.Close();
                FileByteStream = null;
            }
        }   

    }
}

Implementacja interfejsu:

Kopiuj
using System.IO;
using System.ServiceModel.Activation;
using System.Web;
using System.Web.Configuration;

namespace TransferManager
{
    public class TransferService : ITransferService
    {
        private readonly Logger.Logger _logger = new Logger.Logger();

        public void UploadFile(RemoteFileInfo request)
        {
            _logger.Info("Receiving file");
            FileStream targetStream;
            var sourceStream = request.FileByteStream;

            string uploadFolder = WebConfigurationManager.AppSettings["UploadDirectory"];
            if(!Directory.Exists(uploadFolder))
            {
                Directory.CreateDirectory(uploadFolder);
            }
            string filePath = Path.Combine(uploadFolder, request.FileName);
            
            using (targetStream = new FileStream(filePath, FileMode.Create,FileAccess.Write))
            {
                const int bufferLen = 65536;
                var buffer = new byte[bufferLen];
                int count;
                while ((count = sourceStream.Read(buffer, 0, bufferLen)) > 0)
                {
                    targetStream.Write(buffer, 0, count);
                }
                sourceStream.Close();
            }
            _logger.Info("File received");
        }
    }
}
 

Kontroler

Kopiuj
 using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
using FileUploader.Models;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using FileUploader.ServiceReference1;

namespace FileUploader.Controllers
{
    public class HomeController : Controller
    {

        public ActionResult Index()
        {
            return View();
        }

        void progressStream_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            HttpContext.Application[e.Name] = 100 * e.BytesRead / e.Length;
        }

        [HttpPost]
        public string SendFile(HttpPostedFileBase file)
        {
            ThreadPool.QueueUserWorkItem(a => StartUploadingFile(file)); //**Jeżeli wywołam funkcję w ten sposób, to dostaję wyjątek.**
            //StartUploadingFile(file); //**Jeżeli zrobię to tak, to wszystko jest okej.**
            return "Done";
        }

        private void StartUploadingFile(HttpPostedFileBase postedFile)
        {
            if(postedFile == null)
            {
                return;
            }
            var client = new TransferServiceClient();
            var progressStream = new StreamWithProgress(postedFile.InputStream, postedFile.FileName);
            var name = Path.GetFileName(postedFile.FileName);
            progressStream.ProgressChanged += progressStream_ProgressChanged;
            client.UploadFile(name, postedFile.ContentLength, progressStream);

            HttpContext.Application[name] = 0;
        }

        [HttpPost]
        public long UploadingProgress(string filename)
        {
            if (HttpContext.Application.AllKeys.FirstOrDefault(a => a.Equals(filename)) == null)
            {
                return 0;
            }
            long returnValue = Convert.ToInt64(HttpContext.Application[filename]);
            if (returnValue == 100)
            {
                HttpContext.Application.Remove(filename);
            }
            return returnValue;
        }
    }
}

Ponadto używam własnego strumienia, aby móc kontrolować postęp wysyłania pliku

Kopiuj
// Code written by Dimitris Papadimitriou - http://www.papadi.gr
// Code is provided to be used freely but without any warranty of any kind

using System;
using System.IO;

namespace FileUploader.Models
{
    public class StreamWithProgress : Stream
    {
        private readonly Stream _file;
        private readonly long _length;
        private readonly string _name;

        public event EventHandler<ProgressChangedEventArgs> ProgressChanged;

        private long _bytesRead;

        public StreamWithProgress(Stream file, string name)
        {
            _file = file;
            _length = file.Length;
            _bytesRead = 0;
            _name = name;
            if (ProgressChanged != null)
            {
                ProgressChanged(this, new ProgressChangedEventArgs(_bytesRead, _length, _name));
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int result = _file.Read(buffer, offset, count); // **Jeżeli wywołam usługę z drugiego wątku, to tutaj dostaję ObjectDisposedException z treścią "Cannot access a closed file."**
            _bytesRead += result;
            if (ProgressChanged != null)
            {
                ProgressChanged(this, new ProgressChangedEventArgs(_bytesRead, _length, _name));
            }
            return result;
        }

        public double GetProgress()
        {
            return ((double)_bytesRead) / _file.Length;
        }

        public override bool CanRead
        {
            get { return _file.CanRead; }
        }

        public override bool CanSeek
        {
            get { return _file.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return _file.CanWrite; }
        }

        public override void Flush()
        {
            _file.Flush();
        }

        public override long Length
        {
            get { return _file.Length; }
        }

        public override long Position
        {
            get { return _bytesRead; }
            set { _file.Position = value; }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _file.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            _file.SetLength(value);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _file.Write(buffer, offset, count);
        }
    }
}
 

Poniżej configi. Najpierw config usługi WCF:

Kopiuj
<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="UploadDirectory" value="E:\upload\"/>
  </appSettings>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
        <listeners>
          <add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="D:\log\Traces.svclog"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.web>
    <compilation debug="true" targetFramework="4.0"/>
    <httpRuntime maxRequestLength="2147483647" useFullyQualifiedRedirectUrl="true" executionTimeout="14400"/>
  </system.web>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="TransferService" closeTimeout="00:01:00" openTimeout="00:01:00" 
                 receiveTimeout="10:00:00" sendTimeout="00:01:00" allowCookies="true" 
                 maxBufferSize="2147483647" maxReceivedMessageSize="2147483647"
                 messageEncoding="Text" textEncoding="utf-8" 
                 transferMode="Streamed">
          <readerQuotas maxStringContentLength="2147483647" maxDepth="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
          <security mode="None"/>
        </binding>
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="TransferServiceBehavior" name="TransferManager.TransferService">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="TransferService" contract="TransferManager.ITransferService" isSystemEndpoint="false"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TransferServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2147483647"/>
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

A tutaj konfiguracja stronki

Kopiuj
<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=152368
  -->
<configuration>
  <configSections>
    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
    </sectionGroup>
  </configSections>
  <connectionStrings>
    <add name="ApplicationServices" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <customErrors mode="On" defaultRedirect="~/Error">
    </customErrors>
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Helpers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>
    <httpRuntime maxRequestLength="2147483647" useFullyQualifiedRedirectUrl="true" executionTimeout="14400" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880" />
    </authentication>
    <membership>
      <providers>
        <clear />
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    </membership>
    <profile>
      <providers>
        <clear />
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/" />
      </providers>
    </profile>
    <roleManager enabled="false">
      <providers>
        <clear />
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
    </httpModules>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="true">
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
    </modules>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2147483647"></requestLimits>
      </requestFiltering>
    </security>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_ITransferService" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
          maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
          messageEncoding="Text" textEncoding="utf-8" transferMode="Streamed"
          useDefaultWebProxy="true">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="None">
            <transport clientCredentialType="None" proxyCredentialType="None"
              realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://localhost/TransferManager/TransferService.svc"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ITransferService"
        contract="ServiceReference1.ITransferService" name="BasicHttpBinding_ITransferService" />
    </client>
  </system.serviceModel>
  <elmah>
    <!--
        See http://code.google.com/p/elmah/wiki/SecuringErrorLogPages for 
        more information on remote access and securing ELMAH.
    -->
    <security allowRemoteAccess="true" />
  </elmah>
  <location path="elmah.axd" inheritInChildApplications="false">
    <system.web>
      <httpHandlers>
        <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
      </httpHandlers>
      <!-- 
        See http://code.google.com/p/elmah/wiki/SecuringErrorLogPages for 
        more information on using ASP.NET authorization securing ELMAH.

      <authorization>
        <allow roles="admin" />
        <deny users="*" />  
      </authorization>
      -->
    </system.web>
    <system.webServer>
      <handlers>
        <add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
      </handlers>
    </system.webServer>
  </location>
</configuration>

Będę wdzięczny za pomoc.
Pozdrawiam,
Afish

edytowany 1x, ostatnio: Afish
AF
  • Rejestracja:prawie 18 lat
  • Ostatnio:18 dni
2

Problem rozwiązany. W sekcji httpRuntime w web.config stronki MVC trzeba było zwiększyć requestLengthDiskTreshold. Po zmianie cała sekcja wygląda tak:

Kopiuj
<httpRuntime maxRequestLength="2147483647" useFullyQualifiedRedirectUrl="true" executionTimeout="14400"  requestLengthDiskThreshold="2147483647"/>

Teraz wszystko śmiga.
Pozdrawiam,
Afish

edytowany 1x, ostatnio: Afish
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)