Je suis sur le point d'ajouter une section à une application ASP.NET (code VB.NET derrière) qui permettra à un utilisateur de récupérer les données sous forme de fichier Excel, que je générerai en fonction des données de la base de données. Bien qu'il existe plusieurs façons de procéder, chacune a ses propres inconvénients. Comment vous retourneriez-vous les données? Je cherche quelque chose d'aussi propre et simple que possible.
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
Vous pouvez sortir les données sous forme de cellules de tableau html, coller un .xls
ou .xlsx
extension dessus, et Excel l'ouvrira comme s'il s'agissait d'un document natif. Vous pouvez même effectuer des calculs de mise en forme et de formule limités de cette façon, il est donc beaucoup plus puissant que CSV. De plus, la sortie d'une table html devrait être assez facile à faire à partir d'une plate-forme Web comme ASP.Net;)
Si vous avez besoin de plusieurs feuilles de calcul ou de feuilles de calcul nommées dans votre classeur Excel, vous pouvez faire quelque chose de similaire via un schéma XML appelé SpreadSheetML
. C'est pas le nouveau format fourni avec Office 2007, mais quelque chose de complètement différent qui fonctionne depuis Excel 2000. La façon la plus simple d'expliquer comment cela fonctionne est avec un exemple:
<?xml version="1.0"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-Microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-Microsoft-com:office:office"
xmlns:x="urn:schemas-Microsoft-com:office:Excel"
xmlns:ss="urn:schemas-Microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
<DocumentProperties xmlns="urn:schemas-Microsoft-com:office:office">
<Author>Your_name_here</Author>
<LastAuthor>Your_name_here</LastAuthor>
<Created>20080625</Created>
<Company>ABC Inc</Company>
<Version>10.2625</Version>
</DocumentProperties>
<ExcelWorkbook xmlns="urn:schemas-Microsoft-com:office:Excel">
<WindowHeight>6135</WindowHeight>
<WindowWidth>8445</WindowWidth>
<WindowTopX>240</WindowTopX>
<WindowTopY>120</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom" />
<Borders />
<Font />
<Interior />
<NumberFormat />
<Protection />
</Style>
</Styles>
<Worksheet ss:Name="Sample Sheet 1">
<Table ss:ExpandedColumnCount="2" x:FullColumns="1" x:FullRows="1" ID="Table1">
<Column ss:Width="150" />
<Column ss:Width="200" />
<Row>
<Cell><Data ss:Type="Number">1</Data></Cell>
<Cell><Data ss:Type="Number">2</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="Number">3</Data></Cell>
<Cell><Data ss:Type="Number">4</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="Number">5</Data></Cell>
<Cell><Data ss:Type="Number">6</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="Number">7</Data></Cell>
<Cell><Data ss:Type="Number">8</Data></Cell>
</Row>
</Table>
</Worksheet>
<Worksheet ss:Name="Sample Sheet 2">
<Table ss:ExpandedColumnCount="2" x:FullColumns="1" x:FullRows="1" ID="Table2">
<Column ss:Width="150" />
<Column ss:Width="200" />
<Row>
<Cell><Data ss:Type="String">A</Data></Cell>
<Cell><Data ss:Type="String">B</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">C</Data></Cell>
<Cell><Data ss:Type="String">D</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">E</Data></Cell>
<Cell><Data ss:Type="String">F</Data></Cell>
</Row>
<Row>
<Cell><Data ss:Type="String">G</Data></Cell>
<Cell><Data ss:Type="String">H</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
Si vous venez d'un DataTable:
public static void DataTabletoXLS(DataTable DT, string fileName)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Charset = "utf-16";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250");
HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.xls", fileName));
HttpContext.Current.Response.ContentType = "application/ms-Excel";
string tab = "";
foreach (DataColumn dc in DT.Columns)
{
HttpContext.Current.Response.Write(tab + dc.ColumnName.Replace("\n", "").Replace("\t", ""));
tab = "\t";
}
HttpContext.Current.Response.Write("\n");
int i;
foreach (DataRow dr in DT.Rows)
{
tab = "";
for (i = 0; i < DT.Columns.Count; i++)
{
HttpContext.Current.Response.Write(tab + dr[i].ToString().Replace("\n", "").Replace("\t", ""));
tab = "\t";
}
HttpContext.Current.Response.Write("\n");
}
HttpContext.Current.Response.End();
}
D'un Gridview:
public static void GridviewtoXLS(GridView gv, string fileName)
{
int DirtyBit = 0;
int PageSize = 0;
if (gv.AllowPaging == true)
{
DirtyBit = 1;
PageSize = gv.PageSize;
gv.AllowPaging = false;
gv.DataBind();
}
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Charset = "utf-8";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250");
HttpContext.Current.Response.AddHeader(
"content-disposition", string.Format("attachment; filename={0}.xls", fileName));
HttpContext.Current.Response.ContentType = "application/ms-Excel";
using (StringWriter sw = new StringWriter())
using (HtmlTextWriter htw = new HtmlTextWriter(sw))
{
// Create a table to contain the grid
Table table = new Table();
// include the gridline settings
table.GridLines = gv.GridLines;
// add the header row to the table
if (gv.HeaderRow != null)
{
Utilities.Export.PrepareControlForExport(gv.HeaderRow);
table.Rows.Add(gv.HeaderRow);
}
// add each of the data rows to the table
foreach (GridViewRow row in gv.Rows)
{
Utilities.Export.PrepareControlForExport(row);
table.Rows.Add(row);
}
// add the footer row to the table
if (gv.FooterRow != null)
{
Utilities.Export.PrepareControlForExport(gv.FooterRow);
table.Rows.Add(gv.FooterRow);
}
// render the table into the htmlwriter
table.RenderControl(htw);
// render the htmlwriter into the response
HttpContext.Current.Response.Write(sw.ToString().Replace("£", ""));
HttpContext.Current.Response.End();
}
if (DirtyBit == 1)
{
gv.PageSize = PageSize;
gv.AllowPaging = true;
gv.DataBind();
}
}
private static void PrepareControlForExport(Control control)
{
for (int i = 0; i < control.Controls.Count; i++)
{
Control current = control.Controls[i];
if (current is LinkButton)
{
control.Controls.Remove(current);
control.Controls.AddAt(i, new LiteralControl((current as LinkButton).Text));
}
else if (current is ImageButton)
{
control.Controls.Remove(current);
control.Controls.AddAt(i, new LiteralControl((current as ImageButton).AlternateText));
}
else if (current is HyperLink)
{
control.Controls.Remove(current);
control.Controls.AddAt(i, new LiteralControl((current as HyperLink).Text));
}
else if (current is DropDownList)
{
control.Controls.Remove(current);
control.Controls.AddAt(i, new LiteralControl((current as DropDownList).SelectedItem.Text));
}
else if (current is CheckBox)
{
control.Controls.Remove(current);
control.Controls.AddAt(i, new LiteralControl((current as CheckBox).Checked ? "True" : "False"));
}
if (current.HasControls())
{
Utilities.Export.PrepareControlForExport(current);
}
}
}
Ceci est un wrapper gratuit autour de SpreadML - cela fonctionne très bien.
Sur la base des réponses données et de la consultation des collègues, il semble que la meilleure solution consiste à générer un fichier XML ou des tableaux HTML et à le pousser en tant que pièce jointe. Le seul changement recommandé par mes collègues est que les données (c'est-à-dire les tables HTML) peuvent être écrites directement dans l'objet Response, éliminant ainsi la nécessité d'écrire un fichier, ce qui peut être gênant en raison de problèmes d'autorisations, d'E/S contention et garantir la purge programmée.
Voici un extrait du code ... Je n'ai pas encore vérifié cela, et je n'ai pas fourni tout le code appelé, mais je pense qu'il représente bien l'idée.
Dim uiTable As HtmlTable = GetUiTable(groupedSumData)
Response.Clear()
Response.ContentType = "application/vnd.ms-Excel"
Response.AddHeader("Content-Disposition", String.Format("inline; filename=OSSummery{0:ddmmssf}.xls", DateTime.Now))
Dim writer As New System.IO.StringWriter()
Dim htmlWriter As New HtmlTextWriter(writer)
uiTable.RenderControl(htmlWriter)
Response.Write(writer.ToString)
Response.End()
étant donné qu'Excel comprend le HTML, vous pouvez simplement écrire les données sous forme de tableau HTML dans un fichier temporaire avec une extension .xls, obtenir le FileInfo pour le fichier et le récupérer à l'aide
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=" + fi.Name);
Response.AddHeader("Content-Length", fi.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(fi.FullName);
Response.End();
si vous voulez éviter le fichier temporaire, vous pouvez écrire dans un flux en mémoire et réécrire les octets au lieu d'utiliser WriteFile
si l'en-tête content-length est omis, vous pouvez simplement réécrire directement le code HTML, mais cela peut ne pas fonctionner correctement tout le temps dans tous les navigateurs
Personnellement, je préfère la méthode XML. Je vais retourner les données de la base de données dans un ensemble de données, l'enregistrer dans XMl, puis je crée un fichier xslt qui contient une règle de transformation qui formatera un document approprié, et une simple transformation XML terminera le travail. La meilleure partie de cela, vous pouvez formater les cellules, effectuer un formatage conditionnel, configurer les en-têtes et les pieds de page et même définir des plages d'impression.
nous exportons tout le temps des données d'une grille de données vers Excel. Le convertir en HTML puis l'écrire dans un fichier Excel
Response.ContentType = "application/vnd.ms-Excel"
Response.Charset = ""
Response.AddHeader("content-disposition", "fileattachment;filename=YOURFILENAME.xls")
Me.EnableViewState = False
Dim sw As System.IO.StringWriter = New System.IO.StringWriter
Dim hw As HtmlTextWriter = New HtmlTextWriter(sw)
ClearControls(grid)
grid.RenderControl(hw)
Response.Write(sw.ToString())
Response.End()
Le seul problème avec cette méthode était que beaucoup de nos grilles avaient des boutons ou des liens, vous en avez donc également besoin:
'needed to export grid to Excel to remove link button control and represent as text
Private Sub ClearControls(ByVal control As Control)
Dim i As Integer
For i = control.Controls.Count - 1 To 0 Step -1
ClearControls(control.Controls(i))
Next i
If TypeOf control Is System.Web.UI.WebControls.Image Then
control.Parent.Controls.Remove(control)
End If
If (Not TypeOf control Is TableCell) Then
If Not (control.GetType().GetProperty("SelectedItem") Is Nothing) Then
Dim literal As New LiteralControl
control.Parent.Controls.Add(literal)
Try
literal.Text = CStr(control.GetType().GetProperty("SelectedItem").GetValue(control, Nothing))
Catch
End Try
control.Parent.Controls.Remove(control)
Else
If Not (control.GetType().GetProperty("Text") Is Nothing) Then
Dim literal As New LiteralControl
control.Parent.Controls.Add(literal)
literal.Text = CStr(control.GetType().GetProperty("Text").GetValue(control, Nothing))
control.Parent.Controls.Remove(control)
End If
End If
End If
Return
End Sub
J'ai trouvé ça quelque part, ça marche bien.
Je l'ai fait plusieurs fois et à chaque fois, le moyen le plus simple était de simplement renvoyer un fichier CSV (Comma Separated Value). Excel l'importe parfaitement et c'est relativement rapide à faire.
Je recommande bibliothèque de génération Excel open source gratuite qui est basée sur OpenXML
Cela m'a aidé il y a plusieurs mois.
Voici un rapport qui tire d'une procédure stockée. Les résultats sont exportés vers Excel. Il utilise ADO au lieu d'ADO.NET et la raison pour laquelle cette ligne est
oSheet.Cells(2, 1).copyfromrecordset(rst1)
Il fait la plupart du travail et n'est pas disponible sur ado.net.
‘Calls stored proc in SQL Server 2000 and puts data in Excel and ‘formats it
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim cnn As ADODB.Connection
cnn = New ADODB.Connection
cnn.Open("Provider=SQLOLEDB;data source=xxxxxxx;" & _
"database=xxxxxxxx;Trusted_Connection=yes;")
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandText = "[sp_TomTepley]"
cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc
cmd.CommandTimeout = 0
cmd.Parameters.Refresh()
Dim rst1 As ADODB.Recordset
rst1 = New ADODB.Recordset
rst1.Open(cmd)
Dim oXL As New Excel.Application
Dim oWB As Excel.Workbook
Dim oSheet As Excel.Worksheet
'oXL = CreateObject("Excel.application")
oXL.Visible = True
oWB = oXL.Workbooks.Add
oSheet = oWB.ActiveSheet
Dim Column As Integer
Column = 1
Dim fld As ADODB.Field
For Each fld In rst1.Fields
oXL.Workbooks(1).Worksheets(1).Cells(1, Column).Value = fld.Name
oXL.Workbooks(1).Worksheets(1).cells(1, Column).Interior.ColorIndex = 15
Column = Column + 1
Next fld
oXL.Workbooks(1).Worksheets(1).name = "Tom Tepley Report"
oSheet.Cells(2, 1).copyfromrecordset(rst1)
oXL.Workbooks(1).Worksheets(1).Cells.EntireColumn.AutoFit()
oXL.Visible = True
oXL.UserControl = True
rst1 = Nothing
cnn.Close()
Beep()
End Sub
Évitez simplement COM Interop via l'espace de noms Microsoft.Office.Interop. C'est tellement lent, peu fiable et non évolutif. Ne s'applique pas aux masochistes.
Vous pouvez créer des fichiers Excel bien formatés en utilisant cette bibliothèque, assez facilement: http://officehelper.codeplex.com/documentation .
Microsoft Office n'a pas besoin d'être installé sur le serveur Web!
Si vous remplissez un GridView avec des données, vous pouvez utiliser cette fonction pour obtenir les données au format HTML, mais en indiquant au navigateur qu'il s'agit d'un fichier Excel.
Public Sub ExportToExcel(ByVal fileName As String, ByVal gv As GridView)
HttpContext.Current.Response.Clear()
HttpContext.Current.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", fileName))
HttpContext.Current.Response.ContentType = "application/ms-Excel"
Dim sw As StringWriter = New StringWriter
Dim htw As HtmlTextWriter = New HtmlTextWriter(sw)
Dim table As Table = New Table
table.GridLines = gv.GridLines
If (Not (gv.HeaderRow) Is Nothing) Then
PrepareControlForExport(gv.HeaderRow)
table.Rows.Add(gv.HeaderRow)
End If
For Each row As GridViewRow In gv.Rows
PrepareControlForExport(row)
table.Rows.Add(row)
Next
If (Not (gv.FooterRow) Is Nothing) Then
PrepareControlForExport(gv.FooterRow)
table.Rows.Add(gv.FooterRow)
End If
table.RenderControl(htw)
HttpContext.Current.Response.Write(sw.ToString)
HttpContext.Current.Response.End()
End Sub
Private Sub PrepareControlForExport(ByVal control As Control)
Dim i As Integer = 0
Do While (i < control.Controls.Count)
Dim current As Control = control.Controls(i)
If (TypeOf current Is LinkButton) Then
control.Controls.Remove(current)
control.Controls.AddAt(i, New LiteralControl(CType(current, LinkButton).Text))
ElseIf (TypeOf current Is ImageButton) Then
control.Controls.Remove(current)
control.Controls.AddAt(i, New LiteralControl(CType(current, ImageButton).AlternateText))
ElseIf (TypeOf current Is HyperLink) Then
control.Controls.Remove(current)
control.Controls.AddAt(i, New LiteralControl(CType(current, HyperLink).Text))
ElseIf (TypeOf current Is DropDownList) Then
control.Controls.Remove(current)
control.Controls.AddAt(i, New LiteralControl(CType(current, DropDownList).SelectedItem.Text))
ElseIf (TypeOf current Is CheckBox) Then
control.Controls.Remove(current)
control.Controls.AddAt(i, New LiteralControl(CType(current, CheckBox).Checked))
End If
If current.HasControls Then
PrepareControlForExport(current)
End If
i = i + 1
Loop
End Sub
Je voudrais simplement créer un fichier CSV basé sur les données, car je le vois comme le plus propre, et Excel a un bon support pour cela. Mais si vous avez besoin d'un format plus flexible, je suis sûr qu'il existe des outils tiers pour générer de vrais fichiers Excel.
CSV est le moyen le plus simple. La plupart du temps, il est lié à Excel. Sinon, vous devez utiliser les API d'automatisation ou le format XML. Les API et XML ne sont pas si difficiles à utiliser.
En supposant que cela concerne un intranet, où vous pouvez définir des autorisations et mandater IE, vous pouvez générer le côté client du classeur avec JScript/VBScript pilotant Excel . Cela vous donne un formatage Excel natif, sans avoir à essayer d'automatiser Excel sur le serveur.
Je ne suis pas sûr que je recommanderais vraiment cette approche, sauf dans des scénarios assez niches, mais c'était assez courant pendant les heydays classiques ASP.
L'un des problèmes que j'ai rencontrés en utilisant l'une des solutions suggérées ci-dessus qui sont similaires à cette réponse est que si vous poussez le contenu sous forme de pièce jointe (ce que j'ai trouvé le plus propre solution pour les navigateurs non ms), puis ouvrez-le dans Excel 2000-2003, son type est une "page Web Excel" et non un document Excel natif.
Ensuite, vous devez expliquer aux utilisateurs comment utiliser "Enregistrer en tant que type" à partir d'Excel pour le convertir en document Excel. C'est un problème si les utilisateurs doivent modifier ce document, puis le télécharger à nouveau sur votre site.
Ma recommandation est d'utiliser CSV. C'est simple et si les utilisateurs l'ouvrent à partir d'Excel, Excel les invite au moins à l'enregistrer dans son format natif.
J'utilise la route CSV (comme décrit ci-dessus), ou plus souvent ces jours-ci, j'utilise Infragistics NetAdvantage pour générer le fichier. (La très grande majorité du temps où les infrastructures sont en jeu, nous exportons simplement un UltraWebGrid existant, qui est essentiellement une solution à un LOC, sauf si des ajustements de mise en forme supplémentaires sont nécessaires. Nous pourrions également générer manuellement un fichier Excel/BIFF, mais il y en a rarement besoin.)
mec, dans .net, je suppose que vous pourriez avoir un composant qui pourrait le faire, mais dans asp classique, je l'ai déjà fait en créant une table html et en changeant le mime de la page en vnd/msexcel. Je suppose que si vous utilisez une grille et modifiez le type MIME, cela devrait peut-être fonctionner, car la grille est une table html.
La seule façon à toute épreuve d'éviter les triangles verts "Il semble que ces nombres soient stockés sous forme de texte" est d'utiliser le format Open XML. Cela vaut la peine de l'utiliser, juste pour éviter les triangles verts inévitables.
La meilleure méthode que j'ai vue pour les rapports Excel est d'écrire les données en XML avec une extension XML et de les diffuser aux clients avec le type de contenu correct. (application/xls)
Cela fonctionne pour tout rapport nécessitant un formatage de base et vous permet de comparer avec des feuilles de calcul existantes à l'aide d'outils de comparaison de texte.
Voici une solution qui diffuse la table de données au format CSV. Rapide, propre et facile, il gère les virgules en entrée.
public static void ExportToExcel(DataTable data, HttpResponse response, string fileName)
{
response.Charset = "utf-8";
response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250");
response.Cache.SetCacheability(HttpCacheability.NoCache);
response.ContentType = "text/csv";
response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
for (int i = 0; i < data.Columns.Count; i++)
{
response.Write(data.Columns[i].ColumnName);
response.Write(i == data.Columns.Count - 1 ? "\n" : ",");
}
foreach (DataRow row in data.Rows)
{
for (int i = 0; i < data.Columns.Count; i++)
{
response.Write(String.Format("\"{0}\"", row[i].ToString()));
response.Write(i == data.Columns.Count - 1 ? "\n" : ",");
}
}
response.End();
}
Vous pouvez bien sûr toujours opter pour des composants tiers. Personnellement, j'ai eu une bonne expérience avec Spire.XLS http://www.e-iceblue.com/xls/xlsintro.htm
Le composant est assez facile à utiliser dans votre application:
Workbook workbook = new Workbook();
//Load workbook from disk.
workbook.LoadFromFile(@"Data\EditSheetSample.xls");
//Initailize worksheet
Worksheet sheet = workbook.Worksheets[0];
//Writes string
sheet.Range["B1"].Text = "Hello,World!";
//Writes number
sheet.Range["B2"].NumberValue = 1234.5678;
//Writes date
sheet.Range["B3"].DateTimeValue = System.DateTime.Now;
//Writes formula
sheet.Range["B4"].Formula = "=1111*11111";
workbook.SaveToFile("Sample.xls");