如何在写入时使用 sheetjs 或 exceljs 展平/重新编译 Excel 电子表格
How to Flatten / Recompile Excel Spreadsheet Using sheetjs or exceljs on Write
我们使用excel作为客户端的配置文件。但是,我们的进程仅在 linux 个服务器上 运行。我们需要获取一个主文件,用新信息更新所有客户端工作簿,然后提交到 GitLab。然后用户检查它,添加他们自己的更改,提交回 GitLab,然后一个进程将工作簿提升到服务器 A。
这个过程使用 nodeJS 效果很好 (exceljs)
另一台服务器上的另一个进程正在使用 perl 获取工作簿,然后将每个 sheet 保存为 csv 文件。
问题是,写出的是原始作品中的数据sheet,而不是更新后的更改。 perl 和 nodejs 都是如此。 perl 和 nodejs xlsx 到 csv 的代码在 post.
的末尾
尝试过的模块:
perl : Spreadsheet::ParseExcel; Spreadsheet::XLSX;
nodejs: node-xlsx, exceljs
我假设它与 Microsoft 在 excel 包装器中使用 XML 有关,它将旧版本保留为历史,并且由于它是原始的 sheet 名称,它得到拉取而不是更新的最新版本。
当我在 Excel 中手动打开时,新信息的一切都如预期的那样正确。
当我使用“另存为...”而不是“保存”时,perl 进程能够正确地将更新后的工作sheet 写成 csv。所以我们的解决方法是让用户在将他们的额外更改提交到 GitLab 之前始终“另存为..”。我们希望依靠培训,但用户和客户的绝对数量使得相信用户会“另存为...”是不切实际的。
有没有办法在我升级到服务器 A 期间复制“另存为...”,或者至少能够判断文件是否已正确保存?我想坚持使用 excelJS,但我会使用任何必要的东西来复制似乎重新编译工作簿的“另存为...”。
除了 nodejs 之外,我还可以使用 perl、python、ruby - 无论如何 - 以确保 csv 创建过程接受新的更改。
感谢您的宝贵时间和帮助。
#!/usr/bin/env perl
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use File::Basename qw/fileparse/;
use File::Spec;
use Spreadsheet::ParseExcel;
use Spreadsheet::XLSX;
use Getopt::Std;
my %args = ();
my $help = undef;
GetOptions(
\%args,
'excel=s',
'sheet=s',
'man|help'=>$help,
) or die pod2usage(1);
pod2usage(1) if $help;
pod2usage(-verbose=>2, exitstatus=>0, output=>\*STDOUT) unless $args{excel} || $args{sheet};
pod2usage(3) if $help;
pod2usage(-verbose=>2, exitstatus=>3, output=>\*STDOUT) unless $args{excel};
if (_getSuffix($args{excel}) eq ".xls") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLS(file=>$file, sheet=>$args{sheet});
} else {
exit 1;
die "Error: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE. \n";
}
}
elsif (_getSuffix($args{excel}) eq ".xlsx") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLSX(file=>$file, sheet=>$args{sheet});
}
else {
exit 1;
die "\nError: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE.\n";
}
}
else {
exit 5;
}
sub _XLS {
my %opts = (
file => undef,
sheet => undef,
@_,
);
my $aggregated = ();
my $parser = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse($opts{file});
if (!defined $workbook) {
exit 3;
croak "Error: Workbook not found";
}
foreach my $worksheet ($workbook->worksheet($opts{sheet})) {
if (!defined $worksheet) {
exit 2;
croak "\nError: Worksheet name doesn't exist in the Excel File. Please check the WorkSheet Name. \nError: This program is CASE SENSITIVE.\n\n";
}
my ($row_min, $row_max) = $worksheet->row_range();
my ($col_min, $col_max) = $worksheet->col_range();
foreach my $row ($row_min .. $row_max){
foreach my $col ($col_min .. $col_max){
my $cell = $worksheet->get_cell($row, $col);
if ($cell) {
$aggregated .= $cell->value().',';
}
else {
$aggregated .= ',';
}
}
$aggregated .= "\n";
}
}
return $aggregated;
}
sub _XLSX {
eval {
my %opts = (
file => undef,
sheet => undef,
@_,
);
my $aggregated_x = ();
my $excel = Spreadsheet::XLSX->new($opts{file});
foreach my $sheet ($excel->worksheet($opts{sheet})) {
if (!defined $sheet) {
exit 2;
croak "Error: WorkSheet not found";
}
if ( $sheet->{Name} eq $opts{sheet}) {
$sheet->{MaxRow} ||= $sheet->{MinRow};
foreach my $row ($sheet->{MinRow} .. $sheet->{MaxRow}) {
$sheet->{MaxCol} ||= $sheet->{MinCol};
foreach my $col ($sheet->{MinCol} .. $sheet->{MaxCol}) {
my $cell = $sheet->{Cells}->[$row]->[$col];
if ($cell) {
$aggregated_x .= $cell->{Val}.',';
}
else {
$aggregated_x .= ',';
}
}
$aggregated_x .= "\n";
}
}
}
return $aggregated_x;
}
};
if ($@) {
exit 3;
}
sub _getSuffix {
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
sub _convertlwr{
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
var xlsx = require('node-xlsx')
var fs = require('fs')
var obj = xlsx.parse(__dirname + '/test2.xlsx') // parses a file
var rows = []
var writeStr = ""
//looping through all sheets
for(var i = 0; i < obj.length; i++)
{
var sheet = obj[i]
//loop through all rows in the sheet
for(var j = 0; j < sheet['data'].length; j++)
{
//add the row to the rows array
rows.push(sheet['data'][j])
}
}
//creates the csv string to write it to a file
for(var i = 0; i < rows.length; i++)
{
writeStr += rows[i].join(",") + "\n"
}
//writes to a file, but you will presumably send the csv as a
//response instead
fs.writeFile(__dirname + "/test2.csv", writeStr, function(err) {
if(err) {
return console.log(err)
}
console.log("test.csv was saved in the current directory!")
答案是不可能的。为了更新具有 excel 函数的工作簿中的数据,您必须在 Excel 中打开它以便触发公式。就这么简单。
你可以把工作簿拆开,创建自己的javascript函数,运行数据通过它然后写出来,但可能出现的问题太多,不建议这样做。
也许有一天微软会为 linux 发布 linux Excel 引擎 API。但是在不调用 GUI 的情况下,这样的事情仍然不太可能通过命令行工作。
我们使用excel作为客户端的配置文件。但是,我们的进程仅在 linux 个服务器上 运行。我们需要获取一个主文件,用新信息更新所有客户端工作簿,然后提交到 GitLab。然后用户检查它,添加他们自己的更改,提交回 GitLab,然后一个进程将工作簿提升到服务器 A。
这个过程使用 nodeJS 效果很好 (exceljs)
另一台服务器上的另一个进程正在使用 perl 获取工作簿,然后将每个 sheet 保存为 csv 文件。
问题是,写出的是原始作品中的数据sheet,而不是更新后的更改。 perl 和 nodejs 都是如此。 perl 和 nodejs xlsx 到 csv 的代码在 post.
的末尾尝试过的模块: perl : Spreadsheet::ParseExcel; Spreadsheet::XLSX; nodejs: node-xlsx, exceljs
我假设它与 Microsoft 在 excel 包装器中使用 XML 有关,它将旧版本保留为历史,并且由于它是原始的 sheet 名称,它得到拉取而不是更新的最新版本。
当我在 Excel 中手动打开时,新信息的一切都如预期的那样正确。
当我使用“另存为...”而不是“保存”时,perl 进程能够正确地将更新后的工作sheet 写成 csv。所以我们的解决方法是让用户在将他们的额外更改提交到 GitLab 之前始终“另存为..”。我们希望依靠培训,但用户和客户的绝对数量使得相信用户会“另存为...”是不切实际的。
有没有办法在我升级到服务器 A 期间复制“另存为...”,或者至少能够判断文件是否已正确保存?我想坚持使用 excelJS,但我会使用任何必要的东西来复制似乎重新编译工作簿的“另存为...”。
除了 nodejs 之外,我还可以使用 perl、python、ruby - 无论如何 - 以确保 csv 创建过程接受新的更改。
感谢您的宝贵时间和帮助。
#!/usr/bin/env perl
use strict;
use warnings;
use Carp;
use Getopt::Long;
use Pod::Usage;
use File::Basename qw/fileparse/;
use File::Spec;
use Spreadsheet::ParseExcel;
use Spreadsheet::XLSX;
use Getopt::Std;
my %args = ();
my $help = undef;
GetOptions(
\%args,
'excel=s',
'sheet=s',
'man|help'=>$help,
) or die pod2usage(1);
pod2usage(1) if $help;
pod2usage(-verbose=>2, exitstatus=>0, output=>\*STDOUT) unless $args{excel} || $args{sheet};
pod2usage(3) if $help;
pod2usage(-verbose=>2, exitstatus=>3, output=>\*STDOUT) unless $args{excel};
if (_getSuffix($args{excel}) eq ".xls") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLS(file=>$file, sheet=>$args{sheet});
} else {
exit 1;
die "Error: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE. \n";
}
}
elsif (_getSuffix($args{excel}) eq ".xlsx") {
my $file = File::Spec->rel2abs($args{excel});
if (-e $file) {
print _XLSX(file=>$file, sheet=>$args{sheet});
}
else {
exit 1;
die "\nError: Can not find excel file. Please check for exact excel file name and location. \nError: This Program is CASE SENSITIVE.\n";
}
}
else {
exit 5;
}
sub _XLS {
my %opts = (
file => undef,
sheet => undef,
@_,
);
my $aggregated = ();
my $parser = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse($opts{file});
if (!defined $workbook) {
exit 3;
croak "Error: Workbook not found";
}
foreach my $worksheet ($workbook->worksheet($opts{sheet})) {
if (!defined $worksheet) {
exit 2;
croak "\nError: Worksheet name doesn't exist in the Excel File. Please check the WorkSheet Name. \nError: This program is CASE SENSITIVE.\n\n";
}
my ($row_min, $row_max) = $worksheet->row_range();
my ($col_min, $col_max) = $worksheet->col_range();
foreach my $row ($row_min .. $row_max){
foreach my $col ($col_min .. $col_max){
my $cell = $worksheet->get_cell($row, $col);
if ($cell) {
$aggregated .= $cell->value().',';
}
else {
$aggregated .= ',';
}
}
$aggregated .= "\n";
}
}
return $aggregated;
}
sub _XLSX {
eval {
my %opts = (
file => undef,
sheet => undef,
@_,
);
my $aggregated_x = ();
my $excel = Spreadsheet::XLSX->new($opts{file});
foreach my $sheet ($excel->worksheet($opts{sheet})) {
if (!defined $sheet) {
exit 2;
croak "Error: WorkSheet not found";
}
if ( $sheet->{Name} eq $opts{sheet}) {
$sheet->{MaxRow} ||= $sheet->{MinRow};
foreach my $row ($sheet->{MinRow} .. $sheet->{MaxRow}) {
$sheet->{MaxCol} ||= $sheet->{MinCol};
foreach my $col ($sheet->{MinCol} .. $sheet->{MaxCol}) {
my $cell = $sheet->{Cells}->[$row]->[$col];
if ($cell) {
$aggregated_x .= $cell->{Val}.',';
}
else {
$aggregated_x .= ',';
}
}
$aggregated_x .= "\n";
}
}
}
return $aggregated_x;
}
};
if ($@) {
exit 3;
}
sub _getSuffix {
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
sub _convertlwr{
my $f = shift;
my ($basename, $dirname, $ext) = fileparse($f, qr/\.[^\.]*$/);
return $ext;
}
var xlsx = require('node-xlsx')
var fs = require('fs')
var obj = xlsx.parse(__dirname + '/test2.xlsx') // parses a file
var rows = []
var writeStr = ""
//looping through all sheets
for(var i = 0; i < obj.length; i++)
{
var sheet = obj[i]
//loop through all rows in the sheet
for(var j = 0; j < sheet['data'].length; j++)
{
//add the row to the rows array
rows.push(sheet['data'][j])
}
}
//creates the csv string to write it to a file
for(var i = 0; i < rows.length; i++)
{
writeStr += rows[i].join(",") + "\n"
}
//writes to a file, but you will presumably send the csv as a
//response instead
fs.writeFile(__dirname + "/test2.csv", writeStr, function(err) {
if(err) {
return console.log(err)
}
console.log("test.csv was saved in the current directory!")
答案是不可能的。为了更新具有 excel 函数的工作簿中的数据,您必须在 Excel 中打开它以便触发公式。就这么简单。
你可以把工作簿拆开,创建自己的javascript函数,运行数据通过它然后写出来,但可能出现的问题太多,不建议这样做。
也许有一天微软会为 linux 发布 linux Excel 引擎 API。但是在不调用 GUI 的情况下,这样的事情仍然不太可能通过命令行工作。