首页 > 解决方案 > 如何在写入时使用 sheetjs 或 exceljs 展平/重新编译 Excel 电子表格

问题描述

我们使用 excel 作为客户端的配置文件。但是,我们的进程只在 linux 服务器上运行。我们需要获取一个主文件,使用新信息更新所有客户端工作簿,然后提交到 GitLab。然后用户检查它,添加他们自己的更改,提交回 GitLab,然后一个进程将工作簿提升到服务器 A。

这个过程使用 nodeJS ( exceljs )效果很好

另一台服务器上的另一个进程是使用 perl 来获取工作簿,然后将每个工作表保存为 csv 文件。

问题是,写出的是原始工作表中的数据,而不是更新的更改。perl 和 nodejs 都是如此。perl 和 nodejs xlsx 到 csv 的代码在帖子的末尾。

尝试的模块: perl : Spreadsheet::ParseExcel; 电子表格::XLSX; nodejs:node-xlsx,exceljs

我认为它与 Microsoft 在 excel 包装器中使用 XML 有关,它将旧版本保留为历史记录,并且由于它是原始工作表名称,因此它被拉取而不是更新的最新版本。

当我在 Excel 中手动打开时,新信息如预期的那样一切都是正确的。

当我使用“另存为...”而不是“保存”时,perl 进程能够正确地将更新的工作表写为 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!")

标签: node.jscsvperlsave-asexceljs

解决方案


答案是不可能的。为了更新具有 Excel 函数的工作簿中的数据,您必须在 Excel 中打开它才能触发公式。就是这么简单。

您可以将工作簿拆开,创建自己的 javascript 函数,通过它运行数据,然后将其写出来,但是有很多可能的问题,因此不建议这样做。

或许有一天微软会为 linux 发布一个 linux Excel 引擎 API。但是,在不调用 GUI 的情况下,这样的事情仍然不太可能通过命令行工作。


推荐阅读