Cómo evitar Response.End () "Se canceló el hilo" Excepción durante la descarga del archivo de Excel

96

Traté de convertir mi conjunto de datos en Excel y descargar ese Excel. Obtuve mi archivo de Excel requerido. Pero System.Threading.ThreadAbortException se generó en cada descarga de Excel. ¿Cómo resolver este problema? .. Por favor ayúdame ...

Llamo a este método en mi pantalla aspx También hay la misma excepción lanzada por este método.

Llamo a esa función pública void ExportDataSet (DataSet ds) en muchas pantallas aspx y también estoy manteniendo el método de registro de errores para las excepciones que se generan en tiempo de ejecución, esas excepciones se escriben en archivos .txt. Entonces, esa misma excepción se registra en todos los archivos txt de la pantalla aspx. Solo quiero evitar que esta excepción se arroje desde el archivo de clase declarado del método a aspx. Simplemente solo quiero manejar esta excepción en el archivo de clase de declaración de mi método.

Llamada al método de archivo ASPX: excel.ExportDataSet (dsExcel);

Definición del método:

public void ExportDataSet(DataSet ds)
{

   try
   {
      string filename = "ExcelFile.xls";
      HttpResponse response = HttpContext.Current.Response;
      response.Clear();
      response.Charset = "";
      response.ContentType = "application/vnd.ms-excel";
      response.AddHeader("Content-Disposition", "attachment;filename=\"" + filename + "\"");
      using (StringWriter sw = new StringWriter())
      {
         using (HtmlTextWriter htw = new HtmlTextWriter(sw))
         {
             GridView dg = new GridView();
             dg.DataSource = ds.Tables[0];
             dg.DataBind();
             dg.RenderControl(htw);
             // response.Write(style);
             response.Write(sw.ToString());                                                
             response.End();                    // Exception was Raised at here
         }
      }
   }
   catch (Exception ex)
   {
      string Err = ex.Message.ToString();
      EsHelper.EsADLogger("HOQCMgmt.aspx ibtnExcelAll_Click()", ex.Message.ToString());
   }
   finally
   {                
   }
}
user3171957
fuente
2
No use Response.Endvea stackoverflow.com/a/3917180/2864740 (y las otras respuestas); tenga en cuenta que la excepción es "de esperar", ya que es la forma en que se desenrolla la pila (así que no atrape esa excepción). Si aún desea capturar [otras] excepciones, use:.. catch (ThreadAbortException) { throw; /* propagate */ } catch (Exception ex) { .. }
user2864740
Solo por curiosidad, ¿qué registrador está usando
Rogue39nin

Respuestas:

194

Investigué en línea y vi que Response.End()siempre arroja una excepción.

Reemplazar esto: HttpContext.Current.Response.End();

Con este:

HttpContext.Current.Response.Flush(); // Sends all currently buffered output to the client.
HttpContext.Current.Response.SuppressContent = true;  // Gets or sets a value indicating whether to send HTTP content to the client.
HttpContext.Current.ApplicationInstance.CompleteRequest(); // Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event.
usuario3412640
fuente
2
Wow un regalo del cielo. Me ahorró horas de depuración usando WinDbg. En mi caso, mi w3wp.exe simplemente se bloqueó si hay demasiadas ThreadAbortException
Dio Phung
Gracias. Este fragmento de código es realmente útil si desea agregar alguna verificación de autorización al constructor del servicio
asmx
Esto funcionó para mí. Reemplacé .End () con el código sugerido y ahora funciona sin excepción. Gracias, Mi código de trabajo ahora es: Response.ContentType = "text / csv"; Response.AddHeader ("Content-Disposition", string.Format ("adjunto; nombre de archivo = \" {0} \ "", Path.GetFileName (filePath))); Response.TransmitFile (filePath); //Response.End (); HttpContext.Current.Response.Flush (); HttpContext.Current.Response.SuppressContent = true; HttpContext.Current.ApplicationInstance.CompleteRequest ();
Nour Lababidi
3
No. No me funciona. De hecho, mire la respuesta. Si Response.End()no , no trabajar, ¿por qué la respuesta sugerida tiene también Response.End()en la última línea? ¡En cambio, la respuesta de @Binny (abajo) ayuda!
user3454439
1
De acuerdo con la documentación en docs.microsoft.com/en-us/dotnet/api/system.web.httpresponse.end Request.End solo se admite por compatibilidad con versiones anteriores. Se recomienda el uso de CompleteRequest como reemplazo
Rudolf Dvoracek
11

Esto me ayudó a manejar la Thread was being abortedexcepción,

try
{
   //Write HTTP output
    HttpContext.Current.Response.Write(Data);
}  
catch (Exception exc) {}
finally {
   try 
    {
      //stop processing the script and return the current result
      HttpContext.Current.Response.End();
     } 
   catch (Exception ex) {} 
   finally {
        //Sends the response buffer
        HttpContext.Current.Response.Flush();
        // Prevents any other content from being sent to the browser
        HttpContext.Current.Response.SuppressContent = true;
        //Directs the thread to finish, bypassing additional processing
        HttpContext.Current.ApplicationInstance.CompleteRequest();
        //Suspends the current thread
        Thread.Sleep(1);
     }
   }

si usa el siguiente código en lugar de HttpContext.Current.Response.End(), obtendrá una Server cannot append header after HTTP headers have been sentexcepción.

            HttpContext.Current.Response.Flush();
            HttpContext.Current.Response.SuppressContent = True;
            HttpContext.Current.ApplicationInstance.CompleteRequest();

Espero eso ayude

Binny
fuente
1
Funciona para mi. Lo anterior no lo hace. En realidad es curioso que mientras Response.End()no funciona, pero el método sugerido también tiene Response.End()en la última línea?
user3454439
1
Porque estás detectando y ocultando la excepción.
Dan Friedman
3
Qué solución tan horrible
Razor
4

Parece ser la misma pregunta que:

Cuando se llama a ASP.NET System.Web.HttpResponse.End (), ¿se cancela el hilo actual?

Entonces es por diseño. Debe agregar una captura para esa excepción y "ignorarla" con gracia.

robnick
fuente
Llamo a esa función pública void ExportDataSet (DataSet ds) en muchas pantallas aspx y también estoy manteniendo el método de registro de errores para las excepciones que se generan en tiempo de ejecución, esas excepciones se escriben en archivos .txt. Entonces, esa misma excepción se registra en todos los archivos txt de la pantalla aspx. Solo quiero evitar que esta excepción se arroje desde el archivo de clase declarado del método a aspx. Simplemente solo quiero manejar esta excepción en el archivo de clase de declaración de mi método.
user3171957
Por comentario del usuario en su pregunta, simplemente capture TheadAbortException -> catch (ThreadAbortException) {}
robnick
Sí, captura esa excepción con el archivo de clase de declaración de método.
user3171957
4

Mueva Response.End () fuera de los bloques Try / Catch y Using.

Se supone que debe lanzar una excepción para omitir el resto de la solicitud, simplemente no se suponía que debías detectarla.

bool endRequest = false;

try
{
    .. do stuff
    endRequest = true;
}
catch {}

if (endRequest)
    Resonse.End();
Steve
fuente
¿Por qué no colocar eso en un bloque de Finalmente, para que siempre se ejecute?
GoldBishop
podría hacer eso, especialmente si tiene una declaración de retorno en el bloque try. Pero si intentas / atrapar / ignorar, entonces ni siquiera necesitas el finalmente. lo importante es que no debería detectar la excepción ThreadAbortException.
Steve
Es cierto que el TAE es un PITA para devolver una Respuesta exitosa.
GoldBishop
3

Solo pon el

Response.End();

dentro de un bloque finalmente en lugar de dentro del bloque try.

¡¡¡Esto me ha funcionado !!!.

Tuve la siguiente estructura de código problemática (con la excepción)

...
Response.Clear();
...
...
try{
 if (something){
   Reponse.Write(...);
   Response.End();

   return;

 } 

 some_more_code...

 Reponse.Write(...);
 Response.End();

}
catch(Exception){
}
finally{}

y lanza la excepción. Sospecho que la excepción se lanza cuando hay código / trabajo para ejecutar después de la respuesta.End (); . En mi caso, el código adicional fue solo la devolución.

Cuando acabo de mover la respuesta.End (); al bloque finalmente (y dejó el retorno en su lugar, lo que provoca que se salte el resto del código en el bloque try y salte al bloque finalmente (no solo saliendo de la función contenedora)) la Excepción dejó de tener lugar.

Lo siguiente funciona bien:

...
Response.Clear();
...
...
try{
 if (something){
   Reponse.Write(...);

   return;

 } 

 some_more_code...

 Reponse.Write(...);

}
catch(Exception){
}
finally{
    Response.End();
}
usuario2265251
fuente
3

Utilice un bloque de captura especial para la excepción del método Response.End ()

{
    ...
    context.Response.End(); //always throws an exception

}
catch (ThreadAbortException e)
{
    //this is special for the Response.end exception
}
catch (Exception e)
{
     context.Response.ContentType = "text/plain";
     context.Response.Write(e.Message);
}

O simplemente elimine Response.End () si está creando un administrador de archivos

SoliQuiD
fuente
2

Eliminé el botón de enlace del UpdatePanel y también comenté el Response.End () Success !!!

nunopacheco
fuente
1

el error de Response.END (); se debe a que está usando un panel de actualización de asp o cualquier control que use javascript, intente usar el control nativo de asp o html sin javascript o scriptmanager o scripting y vuelva a intentarlo

Iván Rentería Vidal
fuente
1

Esto no es un problema, pero es por diseño. La causa raíz se describe en la página de soporte de Microsoft.

El método Response.End finaliza la ejecución de la página y cambia la ejecución al evento Application_EndRequest en la canalización de eventos de la aplicación. La línea de código que sigue a Response.End no se ejecuta.

La solución proporcionada es:

Para Response.End, llame al método HttpContext.Current.ApplicationInstance.CompleteRequest en lugar de Response.End para omitir la ejecución del código en el evento Application_EndRequest

Aquí está el enlace: https://support.microsoft.com/en-us/help/312629/prb-threadabortexception-occurs-if-you-use-response-end--response-redi

Anjani
fuente
0

descargar la respuesta al cliente antes de response.end ()

Más sobre el método Response.Flush

Así que use el código mencionado a continuación antes response.End();

response.Flush();  
thejustv
fuente
0

Usé todos los cambios anteriores, pero aún así recibía el mismo problema en mi aplicación web.

Luego me comuniqué con mi proveedor de alojamiento y les pedí que verificaran si algún software o antivirus bloquea nuestros archivos para transferirlos a través de HTTP. o ISP / red no permite la transferencia de archivos.

Verificaron la configuración del servidor y omitieron el "Firewall compartido del centro de datos" para mi servidor y ahora nuestra aplicación puede descargar el archivo.

Espero que esta respuesta ayude a alguien. Esto es lo que funcionó para mí

Sushil Jadhav
fuente
Si bien podría funcionar, no parece una solución sólida. ¿Estás diciendo que el firewall está completamente desactivado? Eso sería un gran "no". ¿O está personalizado para su aplicación? También es extraño ver una ThreadAbortException en algo que bloquea el firewall del centro de datos ... En otras palabras, ¿no es una respuesta a la pregunta?
Michael
0

Encontré la razón. Si quita los paneles de actualización, ¡funciona bien!

WSJayaruwan Sumathirathne
fuente
4
debe estar en el comentario
TarangP
0

Recomiendo esta solución:

  1. No use response.End();

  2. Declare esta var global: bool isFileDownLoad;

  3. Justo después de tu (response.Write(sw.ToString());) set ==> isFileDownLoad = true;

  4. Anula tu Render como:

    /// AEG : Very important to handle the thread aborted exception
    
    override protected void Render(HtmlTextWriter w)
    {
         if (!isFileDownLoad) base.Render(w);
    } 
Abdelrahman ELGAMAL
fuente
0

Descubrí que lo siguiente funcionaba mejor ...

   private void EndResponse()
    {
        try
        {
            Context.Response.End();
        }
        catch (System.Threading.ThreadAbortException err)
        {
            System.Threading.Thread.ResetAbort();
        }
        catch (Exception err)
        {
        }
    }
that_roy
fuente
0

Para mí, me ayudó a registrar un botón que llama al código detrás del código como un control de devolución.

protected void Page_Init(object sender, EventArgs e)
{
    ScriptManager.GetCurrent(this.Page).RegisterPostBackControl(btnMyExport);
}
usuario13938019
fuente