Ms excel для сервера

Join us at in developing ExcelPackage

  • Download sample source code

Introduction

The adoption of XML as the native file format for Excel 2007 has opened up a whole host of new possibilities. Now for the first time we can create native Excel files on the server using standard tools. No longer is there a need to pollute your beautiful server room with a headless PC running Excel and an old VB6 app that uses OLE Automation to create reports. Such things can be consigned to the trash heap of IT history.

However, the new Office Open XML standard is so new there are precious few good code samples. This article aims to correct the situation by providing a flexible set of open source class libraries that you can use to create Excel spreadsheets on the server.

Why create Excel spreadsheets on the server?

Excel has long been recognized as the de facto standard when it comes to presenting management reports. The unique combination of great calculation engine, excellent charting facilities and the possibility to perform «what if» analysis, make it the «must have» business intelligence tool.

So when we came to replace our aging management reporting infrastructure, we set one key requirement: the new system must be Web-based and provide a «download in Excel» option. For our business intelligence project we built the data warehouse using SQL Server 2005 populated from our PeopleSoft and Novient implementations using SQL Server 2005 Integration Services (SSIS). The OLAP cube was built on SQL Server 2005 Analysis Services (SSAS). SQL Server 2005 Reporting Services (SSRS) provides the Web-based access to management reports and the all important «download in Excel» option. So why do we need to create Excel on the server?

The problem with SQL Server 2005 Reporting Services is that it the Excel spreadsheets it generates are «dumb». They contain no formula — just the raw data. So the recipient cannot perform a «what if» analysis by changing a few values and expecting the spreadsheet to recalculate.

We considered a number of ways to overcome this issue, but by far the most attractive is to create the Excel spreadsheet on the server, straight from the OLAP cube data. So we created a web-part for SharePoint Server 2007 so the user could enter their criteria and view the report on-line via Excel Services. Of course users can download the file for off-line viewing in Excel 2007 or even Excel 2003 file format. This SharePoint web-part and its associated web service that does the file format conversion will be the topic of another article.

The Open Source ExcelPackage Assembly

The ExcelPackage assembly is a set of classes and wrappers around the .NET 3.0 System.IO.Packaging API and the new SpreadsheetML file format. It extracts away the complexity of dealing with the individual XML components that make up the new Excel 2007 file format. The classes are published as an assembly called ExcelPackage which you can install in the GAC and use as the basis of your own applications. In the sprit of open source projects, if you wish to help extend the functionality offered by the ExcelPackage assembly then join the team over at the ExcelPackage Open XML project.

Creating an Excel spreadsheet from scratch

Sample 1 shows how to create a new Excel spreadsheet containing some basic data and calculations. So let’s see how this is achieved.

using OfficeOpenXml;  
…
FileInfo newFile = new FileInfo(@"C:mynewfile.xlsx"); 
using (ExcelPackage xlPackage = new ExcelPackage(newFile)) { … }

This creates a new instance of the all important ExcelPackage class which gives you access to the Excel workbook and worksheets. If mynewfile.xlsx already exists, then ExcelPackage will open the existing file. Otherwise mynewfile.xlsx will be created from scratch.

Let’s start by adding a new worksheet called “Tinned Goods” and adding some basic data and a simple calculation:

ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets.Add("Tinned Goods");

worksheet.Cell(1, 1).Value = "Product";
…
worksheet.Cell(4, 1).Value = "Peas";
worksheet.Cell(5, 1).Value = "Total";
 

worksheet.Cell(1, 2).Value = "Tins Sold";
 
ExcelCell cell = worksheet.Cell(2, 2);
cell.Value = "15"; 

string calcStartAddress = cell.CellAddress;  
worksheet.Cell(3, 2).Value = "32";  
… 
worksheet.Cell(5, 2).Formula = string.Format("SUM({0}:{1})", 
                                           calcStartAddress, calcEndAddress);

If all this seems a bit too easy — well yes it is! The ExcelPackage assembly does all the hard work of creating the XML elements that are needed to represent an Excel worksheet, the Excel rows, the Excel cells etc. All you need to do is connect in the data! The ExcelWorksheet class has all the properties and methods needed to create and manipulate worksheets. A number of supporting classes (such as ExcelCell, ExcelRow, ExcelColumn, ExcelHeaderFooter etc.) provide properties and methods of each worksheet component. They also provide helper functions that make it easy to manipulate Excel data. For example, the ExcelCell.GetCellAddress(iRow, iColumn) method turns your row and column integers into Excel-style cell addresses.

Ok, so in our sample some of the data is too wide for the column, so let’s change the column size:

worksheet.Column(1).Width = 15;

Next, add some headers and footers to the spreadsheet. Note how we use the PageNumber and NumberOfPages constants to insert codes into the footer text. This causes Excel to insert the page number and the number of pages in the document footer. worksheet.HeaderFooter.oddHeader.CenteredText = «Tinned Goods Sales»;

worksheet.HeaderFooter.oddFooter.RightAlignedText =
string.Format("Page {0} of {1}", ExcelHeaderFooter.PageNumber, 
ExcelHeaderFooter.NumberOfPages);

OK, so let’s write some real hard code. Let’s insert a line into the worksheet so we can add some more data. This will screw up our formula as it will be referencing the wrong set of rows (i.e. the new row will not be included in the total).

worksheet.InsertRow(3);

Well hell no, the formula is correct. The InsertRow method not only updates all the row and cell references in the underlying XML, but also updates all the formulas in the spreadsheet! Ok, we now have our report, but we want to ensure our corporate search engine can find the file later. So let’s add some standard and custom document properties.

xlPackage.Workbook.Properties.Title = "Sample 1";
xlPackage.Workbook.Properties.Author = "John Tunnicliffe"; 
xlPackage.Workbook.Properties.SetCustomPropertyValue("EmployeeID", "1147");

Now save the file and all its components.

xlPackage.Save();

Below is a screenshot of the final output showing the header and the document properties.

Output of Sample 1

Reading data from an Excel spreadsheet

Sample 2 shows how to read data from an existing Excel spreadsheet. We will use the spreadsheet generated by Sample 1 as the source document. To output the contents of column 2 to the console, this is all we need:

using (ExcelPackage xlPackage = new ExcelPackage(existingFile))
{
  
  ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets[1];
  int iCol = 2;  


  
  for (int iRow = 1; iRow < 6; iRow++)
    Console.WriteLine("Cell({0},{1}).Value={2}", iRow, iCol, 
                       worksheet.Cell(iRow, iCol).Value);

  
  Console.WriteLine("Cell({0},{1}).Formula={2}", 6, iCol, 
    
  worksheet.Cell(6, iCol).Formula);
   
} 

Here is the output from the sample code:

Sample 2 Output

Starting with a template

Sample 3 shows how to create a new Excel spreadsheet based on an existing file and populate it with data from a database. This is a much better approach as you can quickly create a spreadsheet with the right formula and the correct corporate ‘look and feel’ using Excel 2007. You can then have the calculations in your template validated by the business before starting to write any code. This whole approach saves a lot of coding time!

Before running the code sample, open the template and take a look at its content. You will see it already has the desired layout and all the formula and formatting required for the title and total lines. However, it only has room for three «data rows» (i.e. rows 5, 6 & 7). You will see how we cope with this later.

Sample 3 template

So let’s start by creating a new Excel spreadsheet based on a template.

using OfficeOpenXml;  
assembly
…
FileInfo newFile = new 
FileInfo(@"C:sample3.xlsx");
FileInfo template = new 
FileInfo(@"C:sample3template.xlsx");
using (ExcelPackage xlPackage = new 
ExcelPackage(newFile, template)) {…}

Behind the scenes, the ExcelPackage constructor simply copies the template and opens the new package. Now obtain a reference to the existing worksheet and initialize some variables:

ExcelWorksheet worksheet = xlPackage.Workbook.Worksheets["Sales"];
ExcelCell cell;
const int startRow = 5;
int row = startRow;

Next open a connection to the database and run the query. This example uses data from the AdventureWorks sample database, so you will need this installed if you want to run the sample.

while (sqlReader.Read())
{
  int col = 1;
  
  
  if (row > startRow) worksheet.InsertRow(row);

  
  
  for (int i = 0; i < sqlReader.FieldCount; i++)
  {
    
    if (sqlReader.GetName(i) == "EmailAddress")
    {
      
      string hyperlink = "mailto:" + sqlReader.GetValue(i).ToString();
      worksheet.Cell(row, 1).Hyperlink = new Uri(hyperlink, UriKind.Absolute);
    }
    else
    {
      
      
      if (sqlReader.GetValue(i) != null)
      worksheet.Cell(row, col).Value = sqlReader.GetValue(i).ToString();
      col++;
    }
  }
  row++;
}

So now we have filled our worksheet with the entire dataset. Note how we use the email address as a hyperlink. Using hyperlinks is useful when you want to link one report up with another.

The purist among you will notice that all the data is written into the cell as a string. However, the Cell(row, col).Value = "xxx" property assignment code checks if the value is a number or a string and sets the cell’s data type accordingly.

As mentioned earlier, the template only has room for three data rows. We cope with this by simply inserting rows into the template — thereby pushing the «Total» row down the sheet. The InsertRow method automatically updates the formula in the «Total» row so that they take into account the extra rows.

As we have inserted a whole set of rows into the spreadsheet, they will not have the correct style. We correct this by simply iterating through the new rows and copying the style from the first row to all the other rows.

for (int iCol = 1; iCol <= 7; iCol++)
{
  cell = worksheet.Cell(startRow, iCol);
  for (int iRow = startRow; iRow <= row; iRow++)
  {
    worksheet.Cell(iRow, iCol).StyleID = cell.StyleID;
  }
}

The Power of Named Styles

Anyone familiar with styling HTML with CSS will understand the power and flexibility of using named styles rather than updating the style of every individual element. With named styles, the look and feel of the whole spreadsheet can be altered by changing one style definition. This capability was introduced in Excel 2003, but Excel 2007 goes one step further and makes it a dream to create a template using named styles.

We apply two built-in named styles to highlight the top achieving and the worst performing sales reps.

worksheet.Cell(startRow, 6).Style = "Good";

worksheet.Cell(row, 6).Style = "Bad";

The biggest problem with named styles in Excel 2007 is that if they are not used in your template, then Excel strips out the definition when the file is saved. This is a real headache. There are two ways to cope with this (1) add extra rows that have styles applied and delete them later (which is the technique used in this sample) or (2) load your own style.xml file which contains all the definitions you want to use.

Shared formula

Excel 2007 has a neat feature which saves a lot of coding when it comes to applying the same formula to a range of cells. A formula in one cell can be marked as «shared» and all cells referenced by the shared formula obtain their own version of the formula. So if cell E5 has the formula D5*12, then cell E6 would have the formula D6*12 etc. etc. To set up a shared formula simply call the CreateSharedFormula method. In the following example, the formula in cell E5 is marked as «shared» and all the other cells in the range E5:E21 are assigned their own variation of the formula.

worksheet.CreateSharedFormula(worksheet.Cell(5, 5), worksheet.Cell(21, 5));

Ensuring your formula are recalculated on File-Open

One problem we came across with Excel 2007 is that it does not automatically re-calculate the spreadsheet when it is re-opened — even when the Calculate option set to automatic! This is because the existing cells in the template have both a formula and a value in the cell. So Excel just assumes the value is correct and does not attempt to re-compute the formula. Of course, we have just added twenty rows of data and updated the formula references in the XML — but Excel has no why of knowing this, so assumes the values must be right!

The only way to force the recalculation is to ensure the cell has no value — just a formula. So the RemoveValue() method becomes very useful for all formula in the worksheet. Hence:

worksheet.Cell(22, 5).RemoveValue();

Because of this phenomenon, we changed the ExcelCell.Formula property assignment code so that it removes the cell’s value when you assign the cell a formula.

The final output of Sample 3 code should look something like this — much more professional than anything that can be achieved starting from scratch.

Sample 3 output

Integrity issues

As soon as you start deleting rows or even worksheets from the package, you have potential for integrity issues. Your formulas will reference cells (or worksheets) that no longer exist. The ExcelPackage assembly does a good job of tidying up after you — but cannot cope with complex situations. You will soon know if you have an integrity problem — Excel will complain bitterly when opening the newly created file.

A classic problem is the calcChain.xml file. This tells Excel in what order the calculations should be processed. So if you delete a row that is referenced by the calcChain, Excel will complain. However, if you simply remove the calcChain.xml from the package, Excel re-creates it when the file is opened — and does not complain! So this is an easy fix. The ExcelPackage assembly does exactly that — deletes the calcChain.xml file from the template so that Excel simply re-creates it when the file is opened.

The ExcelPackage assembly also provides you with direct access to each of the XML documents that make up the package. So you can write your own code to manipulate the XML directly. However, if you choose to do this, be careful to ensure the XML conforms to the new Office Open XML standard. Otherwise, Excel will simply strip out your all hard work as «badly formed».

Debugging your application

If you want to understand exactly what is been written into each of the component XML files by the ExcelPackage assembly, then simply add the following line of code:

xlPackage.DebugMode = true;

This will cause the assembly to output the raw XML files in the same location as the output file. You will see a sub-folder called ‘xl’ and another callled ‘docProps’.

Summary

This article has demonstrated just how easy it is to create Excel-based reports on the server using the open source ExcelPackage assembly. We hope you will be able to join us in extending the functionality of the assembly over at the ExcelPackage Open XML project. There is plenty still to do; charting, conditional formatting, inserting comments, to name just a few!

Good luck with your project!

John is a senior business intelligence developer / designer based in London, UK who likes to get his hands dirty doing real code.

Мало пользователей, да и начинающих программистов, которые знают о возможности Excel подключаться к внешним источникам, и в частности к SQL серверу, для загрузки данных из этих источников. Эта возможность достаточно полезна, поэтому сегодня мы займемся ее рассмотрением.

Функционал Excel получения данных из внешних источников значительно упростит выгрузку данных с SQL сервера, так как Вам не придется просить об этом программиста, к тому же данные попадают сразу в Excel. Для этого достаточно один раз настроить подключение и в случае необходимости получать данные в Excel из любых таблиц и представлений Views, из базы настроенной в источнике, естественно таких источников может быть много, например, если у Вас несколько баз данных.

И для того чтобы более понятно рассмотреть данную возможность, мы это будем делать как обычно на примере. Другими словами допустим, что нам надо выгрузить данные, одной таблицы, из базы SQL сервера, средствами Excel, т.е. без помощи вспомогательных инструментов, таких как Management Studio SQL сервера.

Примечание! Все действия мы будем делать, используя Excel 2010. SQL сервер у нас будет MS Sql 2008.

И для начала разберем исходные данные, допустим, есть база test, а в ней таблица test_table, данные которой нам нужно получить, для примера будут следующими:

Скриншот 1

Эти данные располагаются в таблице test_table базы test, их я получил с помощью простого SQL запроса select, который я выполнил в окне запросов Management Studio. И если Вы программист SQL сервера, то Вы можете выгрузить эти данные в Excel путем простого копирования (данные не большие), или используя средство импорта и экспорта MS Sql 2008. Но сейчас речь идет о том, чтобы простые пользователи могли выгружать эти данные.

Заметка! Если Вас интересует SQL и T-SQL, рекомендую посмотреть мои видеокурсы по T-SQL, с помощью которых Вы «с нуля» научитесь работать с SQL и программировать с использованием языка T-SQL в Microsoft SQL Server.

Настройка Excel для получения данных с SQL сервера

Настройка, делается достаточно просто, но требует определенных навыков и консультации администратора SQL сервера. Вы, конечно, можете попросить программиста настроить Excel на работу или сделать это сами, просто спросив пару пунктов, а каких мы сейчас узнаем.

И первое что нам нужно сделать, это конечно открыть Excel 2010. Затем перейти на вкладку «Данные» и нажать на кнопку «Из других источников» и выбрать «С сервера SQL Server»

Скриншот 2

Скриншот 3

Затем у Вас откроется окно «Мастер подключения данных» в котором Вам необходимо, указать на каком сервере располагается база данных и вариант проверки подлинности. Вот именно это Вам придется узнать у администратора баз данных, а если Вы и есть администратор, то заполняйте поля и жмите «Далее».

Курс по SQL для начинающих

Скриншот 4

Где,

  • Имя сервера – это адрес Вашего сервера, здесь можно указывать как ip адрес так и DNS имя, в моем случае сервер расположен на этом же компьютере поэтому я и указал localhost;
  • Учетные данные – т.е. это логин и пароль подключения к серверу, здесь возможно два варианта, первый это когда в сети Вашей организации развернута Active directory (Служба каталогов или домен), то в этом случае можно указать, что использовать те данные, под которыми Вы загрузили компьютер, т.е. доступы доменной учетки, и в этом случае никаких паролей здесь вводить не надо, единственное замечание что и на MSSql сервере должна стоять такая настройка по проверки подлинности. У меня именно так и настроено, поэтому я и выбрал этот пункт. А второй вариант, это когда администратор сам заводит учетные данные на SQL сервере и выдает их Вам, и в этом случае он должен их Вам предоставить.

Далее необходимо выбрать базу, к которой подключаться, в нашем примере это база test. Также это подключение можно настроить сразу на работу с определенной таблицей или представлением, список таблиц и представлений у Вас будет отображен, давайте мы сделаем именно так и настроем подключение сразу на нашу таблицу test_table. Если Вы не хотите этого, а хотите чтобы Вы подключались к базе и потом выбирали нужную таблицу, то не ставьте галочку напротив пункта «Подключаться к определенной таблице», а как я уже сказал, мы поставим эту галочку и жмем «Далее».

Скриншот 5

В следующем окне нам предложат задать имя файла подключения, название и описание, я например, написал вот так:

Скриншот 6

После того как Вы нажмете «Готово» у Вас откроется окно импорта этих данных, где можно указать в какие ячейки копировать данные, я например, по стандарту выгружу данные, начиная с первой ячейки, и жмем «ОК»:

Скриншот 7

В итоге у меня загрузятся из базы вот такие данные:

Скриншот 8

Т.е. в точности как в базе. Теперь когда, например, изменились данные в этой таблице, и Вы хотите выгрузить их повторно Вам не нужно повторять все заново, достаточно в excel перейти на вкладку «Данные» нажать кнопку «Существующие подключения» и выбрать соответствующее, т.е. то которое Вы только что создали.

Скриншот 9

Вот собственно и все, как мне кажется все достаточно просто.

Таким способом получать данные в Excel из базы SQL сервера очень удобно и главное быстро, надеюсь, Вам пригодятся эти знания полученные в сегодняшнем уроке. Удачи!

Несколько примеров работы из MS SQL Server  с таблицами формата Excel(.xls,.xlsx):

1)С
использованием функции OPENROWSET или с OPENDATASOURCE:

SELECT * FROM OPENROWSET(‘MSDASQL’,

‘Driver={Microsoft Excel Driver (*.xls)};

DBQ=[C:gr_otchet.xls]’, ‘SELECT * FROM [Sheet1$A8:D10000]’

Пример для OPENDATASOURCE
из BOL:

SELECT * FROM OPENDATASOURCE(‘Microsoft.Jet.OLEDB.4.0’,

‘Data
Source=C:DataFolderDocumentsTestExcel.xls;Extended Properties=EXCEL 5.0’
)…[Sheet1$] ;

Одна из
распространенных  проблем, это отсутствие
драйверов под х64 платформу, или установка х32 битных под х64 систему. Например,
драйверов Microsoft.Jet.OleDB нет 64 битных,в этом случае  можно использовать другие драйвера, к примеру
 Microsoft.ACE.OLEDB.12.0.

Не забудьте только про
Примечание из  BOL:

Функция
OPENROWSET  или  OPENDATASOURCE может быть использована
для доступа к удаленным данным из источников данных OLE DB только в том случае,
если для заданного поставщика параметр реестра DisallowAdhocAccess явно
установлен в 0 и включен параметр Ad Hoc Distributed Queries расширенной
настройки. Если эти параметры не установлены, поведение по умолчанию запрещает
нерегламентированный доступ.

Если параметр
Ad Hoc Distributed Queries выключен, то  об будет информационное сообщение. Включение
параметра осуществляется через хранимую процедуру sp_configure.

sp_configure Ad Hoc Distributed Queries, 1;

RECONFIGURE;

GO

2) Второй спосб через Linkedserver
и ODBC драйвер.

1-ый способ
хорошо, когда необходимо использовать разово, для частого и широко
использования лучше использовать технологию связанного сервера(Linked Server)

Для этого
необходимо установить на сервере MS SQL Server ODBC драйвер а для Excel, затем создать источник
данных( Администрирование ->Источники данных)

Указывает имя источника данных
,версию Excel и сам
файл.

Сохраняем источник.

После этого создаем связанный
сервер LinkedServed (связанный
сервер):

Указываем имя нашего  связанного сервера и имя созданного нами
раннего ODBC источника.

Сохраняем.

Теперь можно выполнять запросы к
нашему связанному серверу, к примеру:

select * from openquery(excel,‘select * from [sheet1$]’) – получение всех данныз из экселя

select * from openquery(excel,‘select * from
[Sheet1$A10:D2]’
) –
получение данных диапозона $A10:D2

select * from openquery(excel,‘select * from
[Sheet1$A10:D]’
) –
получение данных диапозона с A10:D до
конца файла.

3) Еще одни,
способ, когда необходим импорт разово,
то можно использовать «SQL Server Import and Export Wizard»:

Выделяем БД,  правая кнопка, Задачи, Выбираем пункт Импорт
или Экспорт:

Выбираем источник данных, наш
файл Excel, версию Excel. :

Выбираем куда
копировать данные, указываем таблицу назначение .

После этого
можно пакет запустить немедленно или его сохранить для дальнейшего
использования.

4) Кстати, 4 способ, это как раз создание пакета SSIS в Microsoft Visual Studio, результатом которого
так же будет пакет, похожий на то, что было создано в варианте 3

Делается он
просто

Выбирается Элемент потока
управления

Затем выбирается источник и
сервер назначения:

В источнике соединений создается
новое соединение с нашим файлом Excel,
в  Назначение указываем наш MS SQL Server, указываем таблицу,
сопоставляем столбцы:

После этого сохраняем пакет, и
его запускаем.

Пакет создали  и должен работать.

Удачи .

Время на прочтение
6 мин

Количество просмотров 28K

Не секрет, что зачастую PHP-программистам приходится решать задачи, весьма далёкие от бытового представления о «веб-разработке». Развитие языка в последние годы привело к тому, что PHP всё чаще считают языком общего назначения, пригодным не только для сайтов, но и для других задач.

Одну из таких «других» задач мне с коллективом пришлось решать совсем недавно. Мы решили поделиться «картой граблей» с теми, кто, возможно, пойдет по этому же пути.

Дано

  • Многие наши партнёры (скажем прямо — это крупные банки) любят считать что-то в Excel. Причем «любят» — это очень нежно сказано. Сложнейшие скоринговые модели могут быть «запрограммированы» в Excel, в файле из сотни листов с десятками макросов
  • Перевести «программы», написанные в Excel на какой-либо язык программирования — практически нереально. Это займет уйму времени, а проблема постоянного обновления и проверки корректности делает такую задачу и вовсе нерешаемой

Требуется

  • Основная информационная система нашей компании написана на PHP. Она содержит в себе как веб-интерфейсы, так и множество консольных сервисов и воркеров.
  • С этими «программами» в Excel нужно как-то взаимодействовать из консольных приложений на PHP — передавать в них данные, обсчитывать, получать результаты

Некоторое время нам хватало возможностей популярной библиотеки PHPExcel. Но когда от бизнеса поступило очередное требование «нужно, чтобы работали макросы, и еще бы хорошо всё это сохранять в PDF», стало понятно, что выбранный путь — тупиковый. Нужно не парсить файлы xlsx, не имитировать просчёт, и даже не использовать Open Office, а научиться взаимодействовать с «настоящим» Microsoft Excel.

В результате недолгих изысканий было решено создать внутренний микро-веб-сервис, который умел бы принять данные, открыть указанный файл Excel, вставить в него принятые данные, просчитать результат и выдать его в качестве ответа клиенту. Заинтересованность в таком веб-сервисе выразили несколько внутренних проектов и работа закипела.

Сервер под Windows? А почему бы нет!

Первым под удар попал отдел dev-ops. Им предстояло подготовить сервер для будущего сервиса. Дело было необычным, поскольку актуального опыта работы с Windows ни у кого нет…

В качестве серверной платформы был выбран Windows Server 2012 R2 standart. Нужно сразу отметить, что «из коробки» Windows совершенно не приспособлена к хостингу приложений на PHP. Требовалось доведение системы до нужного уровня.

Для начала был установлен PowerShellServer. Это позволило нам подключаться к windows-серверу по привычному всем протоколу ssh, не изобретая велосипедов. Поддерживается авторизация по ключам, работает rsync (это важно). Жаль, что в Personal Edition ограничение только на одно одновременное подключение, но для нас это некритично.

Nginx был установлен штатным образом. Взят со страницы nginx.org/ru/download.html Имейте в виду — под windows есть существенные ограничения: только один рабочий процесс, который держит не более 1024 соединений. Впрочем, это опять же было некритично для внутреннего микро-сервиса.

PHP 7.0.9 взят с windows.php.net/download, установлен штатным образом.

Для упрощения перезапуска всего этого «добра» был написан несложный cmd-файл:

cd C:nginx
taskkill /f /IM nginx.exe
taskkill /f /IM php-cgi.exe
 
rm C:nginxlogs*
start nginx
start -WindowStyle Hidden phpphp-cgi -A "-b 0.0.0.0:9000 -c C:serverphpphp.ini"

Первоначальная настройка сервера закончилась успешным выводом страницы с phpinfo(). Однако это было еще только самое начало…

Настраиваем сборку на Windows или Как наступить на все подводные камни?

Мы внутри компании используем Continuous Integration. Всегда. Для любого, сколь угодно малого проекта. Примерный план развертывания выглядит так:

  • Сервер Teamcity следит за изменениями в нужных ветках репозитория (в данном конкретном случае workflow был упрощен до предела и ветка была фактически одна — master)
  • Он же запускает сборку проекта при появлении изменений:
    • Содержимое репозитория с сервера TeamCity с помощью rsync доставляется на целевой сервер, во временную папку (это даёт нам возможность сэкономить на агентах TeamCity)
    • Там же, с помощью ssh, запускается билд-скрипт на phing, который и делает основную работу:
      • Переносит код в постоянное место
      • Устанавливает зависимости через composer
      • Раскладывает конфиги
      • Применяет миграции и так далее…
      • И, наконец, переключает симлинк current (это у нас wwwroot), на новую папку

Что потребовалось далее? ssh-сервер уже установлен, rsync выполняется корректно. Установим phing:

  • Исполняемый файл (phing.phar) берем с www.phing.info/trac/wiki/Users/Installation
  • Аккуратный и красивый bat-ник можно взять на www.phing.info/trac/browser/bin/phing.bat
  • Не забываем добавить путь до phing в PATH, чтобы получить system-wide команду

Git for Windows берем с git-scm.com, устанавливаем, проверяем корректную работу.

Точно по такой же схеме поступаем с composer, только bat-файл пишем сами и он будет значительно проще:

@echo off
if "%PHPBIN%" == "" set PHPBIN=C:serverphpphp.exe
"%PHPBIN%" "C:nginxphpcomposer.phar" %*

Вроде бы всё готово. Запускаем сборку… fail!

Причина 1. Нужно установить расширение php_openssl.dll, иначе Phing не сможет работать с репозиториями через SSL. Проблем не доставило.

Причина 2. Более серьезная. В нашем сценарии сборки используется техника переключения симлинка на папку со свежей сборкой на последнем шаге. Примерно так:

<symlink target="${current.dir}" link="${home.dir}/${build.branch}/current" overwrite="true" />

В результате получается что-то вроде
symlink: "c:serverdomainsthis.servicemastercurrent" => "c:serverdomainsthis.servicemaster2016-04-01-12-34-56"

Оказалось, что создать символическую ссылку на NTFS — не проблема. Проблема ее удалить… Отчего-то операция удаления симлинка требует прав администратора, которых у обычного PHP нет и быть не может.

Нам помогла утилита junction ( technet.microsoft.com/en-us/sysinternals/bb896768 ). С ней вышеуказанный кусок сценария стал выглядеть примерно так:

<exec command="junction -d  ${home.dir}/${build.branch}/current" checkreturn="true" passthru="true" />
<symlink target="${current.dir}" link="${home.dir}/${build.branch}/current" overwrite="true" />

Итак, всё встало на свои места, сборка заработала, как ей и положено. Настала пора писать код!

COM-объекты в PHP

Надо отметить, что собственно код сервиса не доставил никаких проблем.

Как запустить приложение Microsoft Excel и загрузить в приложение существующий файл?

namespace AppComponents;

class Excel
{

    protected $xls;
    
    public function __construct($filename = null)
    {
        $this->xls = new COM("Excel.Application");
        // @todo: выключить, если не требуется видеть работу приложения
        $this->xls->Application->Visible = 1;
        $this->xls->DisplayAlerts = 0;
        if (empty($filename)) {
            $this->xls->Workbooks->Add();
        } else {
            $this->xls->Workbooks->Open($filename);
        }
        $this->xls->Workbooks[1]->Activate();
    }
}

Как закрыть приложение после окончания работы скрипта?

    public function __destruct()
    {
        $this->xls->Workbooks[1]->Close(false);
        $this->xls->Quit();
    }

Получить список всех именованных диапазонов?

    public function getNames()
    {
        $names = $this->xls->Names;
        if ($names->Count == 0) {
            return [];
        } else {
            $ret = [];
            foreach ($names as $name) {
                $ret[$name->Name] = $name->Value;
            }
            return $ret;
        }
    }

Установить значение ячейки или диапазона?

    public function setValue($range, $value)
    {
        $this->xls->Range($range)->Value = iconv('UTF-8', 'Windows-1251', $value);
    }

Прочесть значение из ячейки или диапазона?
Возвращается либо скалярное значение из одной ячейки, либо массив значений, если мы запрашиваем диапазон.

    public function getValue($range)
    {
        $range = $this->xls->Range($range);
        if ($range->Count == 1) {
            $val = $range->Value;
            return is_string($val) ? iconv('Windows-1251', 'UTF-8', $val) : $val;
        } else {
            $ret = [];
            foreach ($range as $cell) {
                $val = $cell->Value;
                $ret[$cell->Address] = is_string($val) ? iconv('Windows-1251', 'UTF-8', $val) : $val;
            }
            return $ret;
        }
    }

Экспортировать книгу в PDF?

    const FORMATS = [
        'PDF' => 0
    ];

    public function saveAs($filename, $format = self::FORMATS['PDF'])
    {
        // Будь проклят тот день, когда разработчики MS-DOS придумали обратные слэши!
        $this->xls->Workbooks[1]->ExportAsFixedFormat($format, str_replace('/', '\', $filename));
    }

Что надо сделать, чтобы вся эта безумная магия заработала?
Добавить расширение php_com_dotnet.dll

Вместо заключения

Довольно феерично наблюдать за сервером: при приходе запроса мгновенно открывается Excel, запускается экспорт и потом также мгновенно всё это хозяйство закрывается.

Удачи и не наступайте на те же грабли!

Литература

  1. php.net/manual/en/book.com.php
  2. msdn.microsoft.com/ru-ru/library/wss56bz7.aspx
  3. geektimes.ru/post/50878

P.S.

По итогам обсуждения в комментариях я решил добавить к статье несколько важных дополнений.

1. Нет, задача не сводится к тому, чтобы какой-то библиотекой для PHP уметь писать в файлы формата Excel или читать данные из них. Статья не об этом. Задача — запускать расчеты, алгоритм которых задан извне в виде файлов Excel (и нет никакой возможности алгоритм преобразовать во что-то другое), подавать на вход данные, получать ответы, формировать отчет. Для этой цели нет другого решения, кроме как запустить файл в «родном» приложении Microsoft Excel.

2. Файлов — сотни. Запросов на расчеты (то есть на запуск таких файлов) — тысячи в день. Это приводит нас к невозможности решения «а пусть кто-то сам вручную запускает эти файлы». Требуется полная и надежная автоматизация.

3. См. предыдущий пункт. Перевод алгоритмов на какой-либо язык программирования невозможен, поскольку затраты на верификацию и QA превысят затраты на windows-сервер на три порядка.

4. Веб-сервис написан для использования другими сервисами, а не клиентами-людьми.

5. Немного изменил метод public function getValue($range), чтобы показать, как прочесть диапазон ячеек, как одно целое. Добавил метод получения списка всех именованных диапазонов.

Наверх

Содержание

  • 1 Подключение с помощью Microsoft.ACE.OLEDB.12.0
  • 2 Линковка Excel к MSSQL
  • 3 Подключение с помощью Microsoft Jet 4.0 Ole DB Provider

Подключение с помощью Microsoft.ACE.OLEDB.12.0

Провайдер данных Microsoft.ACE.OLEDB.12.0 подходит как для 32-битных, так и для 64 -битных версий SQL-сервера.

В данном параграфе описывается пример подключения Excel-файла sample.xlsx как связанного сервера на примере MSSQL 2008 SP3 x64.

Шаг 1. Для начала работы скачайте установочные файлы провайдера Microsoft.ACE.OLEDB.12.0 по ссылке http://www.microsoft.com/en-us/download/details.aspx?id=13255.

Если у вас 64-битная версия SQL Server используйте установочный файл AccessDatabaseEngine_x64.exe.

Excel 001.png

Шаг 2. Для установки провайдера используйте запуск файла через командную строку с ключом /passive.

C:AccessDatabaseEngine_x64.exe /passive 

Excel 004.png Excel 002.png

Шаг 3. Получить данные напрямую из файла можно с помощью команды OPENROWSET

 select * from OPENROWSET('Microsoft.ACE.OLEDB.12.0', 
		          'Excel 8.0; HDR=Yes; Database=C:Sample.xlsx', 
       		          'Select  * from [Sheet1$]')

Изменить данные можно с помощью запроса:

 UPDATE OPENROWSET('Microsoft.ACE.OLEDB.12.0', 
		   'Excel 8.0; HDR=Yes; Database=C:Sample.xlsx', 
		   'Select  * from [Sheet1$]')
 SET id='123456'
 WHERE phone='84952294989'

Убедитесь, что в Excel есть указанное поле [id]. Часто бывает, что столбцы называются [F15]

Ввести строку можно с помощью запроса:

 INSERT INTO OPENROWSET('Microsoft.ACE.OLEDB.12.0', 
			'Excel 12.0;Database=C:Sample.xlsx', 
			'Select * from [Sheet1$]')
 SELECT id, phone, comment from sqltable

Количество столбцов в Excel и в возвращаемом запросе select должно совпадать.

ВНИМАНИЕ: При возникновении ошибки Cannot get the column information from OLE DB provider «Microsoft.ACE.OLEDB.12.0» for linked server «(null)» выполните следующий запрос:

USE [master]
GO
EXEC master . dbo. sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0' , N'AllowInProcess' , 1
GO
EXEC master . dbo. sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0' , N'DynamicParameters' , 1
GO

ВНИМАНИЕ: При возникновении ошибки SQL Server заблокировал доступ к STATEMENT «OpenRowset/OpenDatasource» компонента «Ad Hoc Distributed Queries», поскольку он отключен в результате настройки конфигурации безопасности сервера выполните поочередно два запроса.

sp_configure 'show advanced options', 1;
GO
reconfigure
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
GO
reconfigure
GO

Используемые материалы: http://habrahabr.ru/post/219415/ и http://social.msdn.microsoft.com

Линковка Excel к MSSQL

Чтобы прилинковать Excel файл выполните следующие запросы в SQL Server Management Studio

exec sp_addlinkedserver @server = 'XlsLnkSrv', 
@srvproduct = 'ACE 12.0', 
@provider = 'Microsoft.ACE.OLEDB.12.0', 
@datasrc = 'C:Sample.xlsx', 
@provstr = 'Excel 12.0; HDR=Yes'

где

  • XlsLnkSrv — название связанного сервера
  • C:Sample.xlsx — абсолютный путь к требуемому Excel-файлу

Excel 003.png

Если все выполнено правильно, то после обновления обозревателя объектов вы увидите название установленного поставщика Microsoft.ACE.OLEDB.12.0 в Объекты сервера / Связанные серверы / Поставщики

Excel 005.png

Считать данные можно с помощью команды openquery:

select * from openquery (XlsLnkSrv, 'Select * from [Sheet1$]')

Подключение с помощью Microsoft Jet 4.0 Ole DB Provider

Скачайте установочные файлы провайдера по ссылке http://www.microsoft.com/en-us/download/details.aspx?id=23734.

Технология Jet 4.0 работает только на 32-битных версиях MSSQL.

Для создания связанного сервера используйте следующий SQL-запрос:

EXEC sp_addlinkedserver 'ExcelSource', 'Jet 4.0', 'Microsoft.Jet.OLEDB.4.0', 
'c:MyDataDistExcl.xls', NULL, 'Excel 8.0'  

EXEC sp_addlinkedsrvlogin 'ExcelSource', 'false', NULL, NULL </pre>

где

  • ExcelSource — название связанного сервера
  • c:MyDataDistExcl.xls — абсолютный путь к Excel-файлу

Для получения данных из Excel файла используется запрос:

Select * From [ExcelSource]...[Лист1$]

Like this post? Please share to your friends:
  • Moving pictures for word
  • Ms excel для решения управленческих задач
  • Moving picture boxes in word
  • Ms excel для построения графика по данным электронной таблицы следует использовать
  • Moving picture box in word