【问题标题】:CSV file generation in every 2 hours using Java使用 Java 每 2 小时生成 CSV 文件
【发布时间】:2019-02-27 21:13:14
【问题描述】:

我在我们的项目中要求使用 Java 程序每 2 小时生成一个 CSV 文件。 我想在基于小时的 CSV 文件下创建日期明智的文件夹。这意味着每个 CSV 文件都包含 2 小时(可配置)数据。我能够创建每小时的 CSV 文件。但我无法创建可配置的每小时 CSV。我没有使用 ScheduledExecutorService。我想要简单的方法来解决这个问题。

public static boolean writeFileHourlyBasis(String tenantName, String clusterName, String contentSourceName,
        String fileContent) {

    String reconciliationLogPath = AppConfigHashMap.getPropertyValue("ReconciliationLogFolder");

    if (reconciliationLogPath != null) {
        File fullReconciliationLogPath = new File(reconciliationLogPath + File.separator + tenantName
                + File.separator + clusterName + File.separator + contentSourceName + File.separator + formatter.format(new Date()));
        if (fullReconciliationLogPath.mkdirs() == true){

            logger.debug("Folder " + fullReconciliationLogPath + " created.");
        }
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("HH");
            String fileName = tenantName + "_" + dateFormat.format(new Date()) + ".csv";
            File file1 = new File(fullReconciliationLogPath, fileName);
            String headerString = "";           

            if (!file1.exists()) {
                headerString = "DateTimeUTC,DateTime,Transcript->TranscriptId,Target->ReturnCode,Target->Status,Target->Message,MessageId,StatusMessage \n";
            }else{
                if(isNewLine){
                    headerString="\n";
                }
                isNewLine=false;
            }
            FileWriter datawriter = new FileWriter(file1, true);
            datawriter.append(headerString);
            datawriter.append(fileContent);
            datawriter.append("\n");
            datawriter.flush();
            datawriter.close();
        } catch (Exception e) {
            logger.warn("Exception occurred while writing Reconcilation log :" + e.getMessage());
            return false;
        }
    }else{
        //TODO: log.error("Reconciliation log enabled, but path not provided!");
        logger.warn("Reconciliation log enabled, but path not provided!");
        return false;
    }
    return true;

}

谁能帮我解决这个问题。

【问题讨论】:

  • 或者,您可以使用诸如安排一个 cron 作业,该作业每隔 X 分钟、几小时、几天……运行一次 Java 类...
  • 我建议你避免使用SimpleDateFormat 类。它不仅过时了,而且出了名的麻烦。今天我们在java.time, the modern Java date and time API 中做得更好。 LocalTime.now(yourTimeZoneId).format(DateTimeFormatter.ofPattern("HH")).

标签: java csv date


【解决方案1】:

我没有使用 ScheduledExecutorService。我想要简单的方法来解决这个问题。

您已经有了答案:将 Executors 框架添加到 Java 中是为了解决这个问题的简单方法。执行器抽象出处理后台任务和线程的棘手杂乱细节。

您应该使用ScheduledExecutorService 来达到您的目的。

Executors 乍一看可能让人望而生畏,但在实践中它们的使用却非常简单。阅读 Oracle 教程。然后看看 Stack Overflow 和其他博客等中的代码示例。

提示:使用外部搜索引擎(例如 DuckDuckGo/Bing/Google 和 site:StackOverflow.com 标准)搜索 Stack Overflow。 Stack Overflow 中的内置搜索功能乏善可陈,并且偏向于问题而不是答案。

定义由线程池支持的执行器服务。

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

将您的任务定义为Runnable

Runnable task = new Runnable() {
    @Override
    public void run () {
        … stuff to do goes here
    }
}

Runnable 对象传递给执行器服务。告诉它多久运行一次。您还通过了初始延迟;这里我们通过零立即开始。

scheduledExecutorService.scheduleAtFixedRate( task , 0 , 2 , TimeUnit.HOURS ); 

提示:关于使用 ScheduledExecutorService (SES) 需要了解的两个重要事项:

  • 在不再需要或应用退出时始终优雅地关闭 SES。否则线程池可能会存活下来并继续工作,就像僵尸一样。
  • 始终捕获提交给 SES 的任务中的所有异常,可能还有错误。如果有任何异常或错误冒泡到达 SES,则 SES 的所有进一步工作都会增加。停止是无声的,没有警告,没有日志。

所以修改上面看到的Runnable

Runnable task = new Runnable() {
    @Override
    public void run () {
        try {
            … stuff to do goes here
        } catch ( Exception e ) {
            … Handle any unexpected exceptions bubbling up to avoid silently killing your executor service. 
        }
    }
}

提示:切勿使用糟糕的旧日期时间类,例如 DateCalendarSimpleDateFormat。它们在几年前被现代 java.time 类所取代。


提示:在文件名中写入时刻时,请遵循ISO 8601 标准将日期时间值表示为文本。

java.time 类在解析或生成字符串时默认使用这些格式。

对于文件名,您可能希望使用标准中定义的替代“基本”格式。 基本表示将分隔符的使用减到最少。

请务必避免使用反斜杠、正斜杠和冒号字符。这些分别在 DOS/Windows、Unix 和 macOS/iOS 文件系统中被禁止。


提示:不要编写自己的 CSV 代码。使用 Apache Commons CSV 库读取和写入 CSV 或制表符分隔的文件。根据我的经验,它运作良好。


示例应用

这是一个包含在单个 .java 文件中的完整示例。

我们的小班级Event 有一些对象。在每次计划运行时,我们都会更新每个 Event 对象的时间戳。然后我们使用 Apache Commons CSV 库将所有​​这些 Event 对象的所有成员变量写入 CSV 格式的文本文件。每次运行都由 Runnable 对象定义。

运行由ScheduledExecutorService 调度,由具有单个线程的线程池支持。

在实际工作中,我不会将所有这些压缩到一个 .java 文件中。但是在这里这样做可以得到一个很好的紧凑演示。

package com.basilbourque.example;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ExportToCsv {
    public static void main ( String[] args ) {
        ExportToCsv app = new ExportToCsv();
        app.doIt();
    }

    private void doIt () {
        System.out.println( "TRACE - doIt running at " + ZonedDateTime.now() );
        List< Event > events = List.of(
        new Event( UUID.randomUUID() , "alpha" , Instant.now() ) ,
        new Event( UUID.randomUUID() , "beta" , Instant.now() ) ,
        new Event( UUID.randomUUID() , "gamma" , Instant.now() )
        );

        Runnable task = new Runnable() {
            @Override
            public void run () {
                // Nest all this stuff of your `run` method into a `try-catch( Exception e )` to avoid having your executor cease silently.
                Instant start = Instant.now();
                System.out.print( "TRACE - Runnable starting at " + start + ". " );
                // Update the moment recorded in each `Event` object.
                events.forEach( ( event ) -> event.update() );
                // Export to CSV. Using “Apache Commons CSV” library. https://commons.apache.org/proper/commons-csv/
                // Get current moment in UTC. Lop off the seconds and fractional second. Generate text without delimiters.
                String dateTimeLabel = OffsetDateTime.now( ZoneOffset.UTC ).truncatedTo( ChronoUnit.MINUTES ).format( DateTimeFormatter.ofPattern( "uuuuMMdd'T'HHmmX" , Locale.US ) );
                String fileNamePath = "myCsv_" + dateTimeLabel + ".csv";
                try (  // Try-with-resources syntax automatically closes any passed objects implementing `AutoCloseable`, even if an exception is thrown.
                BufferedWriter writer = new BufferedWriter( new FileWriter( fileNamePath ) ) ;
                CSVPrinter csvPrinter = new CSVPrinter( writer , CSVFormat.DEFAULT.withHeader( "Id" , "Name" , "When" ) ) ;
                ) {
                    for ( Event event : events ) {
                        csvPrinter.printRecord( event.id , event.name , event.when );
                    }
                    csvPrinter.flush();
                } catch ( IOException e ) {
                    // TODO: Handle i/o exception when creating or writing to file in storage.
                    e.printStackTrace();
                }
                Instant stop = Instant.now() ;
                System.out.println( "Runnable ending its run at " + start + ". Duration: " + Duration.between( start , stop ) + ".");
            }
        };

        // Schedule this task. Currently set to run every two minutes, ending after 20 minutes. Adjust as desired.
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();  // Using a single thread here, as we have only a single series of tasks to be executed, no multi-tasking. 
        try {
            scheduledExecutorService.scheduleAtFixedRate( task , 0 , 2 , TimeUnit.MINUTES );  // Schedule our task to run every so often.
            try {
                Thread.sleep( TimeUnit.MINUTES.toMillis( 20 ) );  // Sleep this main thread for a while to let our task running on the background thread do its thing a few times.
            } catch ( InterruptedException e ) {
                System.out.println( "TRACE - Our main thread was woken earlier than expected, and interrupted our run. " );
                e.printStackTrace();
            }
        } finally {
            System.out.println( "Shutting down the scheduledExecutorService at " + ZonedDateTime.now() );  // Generally best to log in UTC, `Instant.now()`.
            scheduledExecutorService.shutdown();  // Always shutdown your executor, as it may otherwise survive your app exiting, becoming a zombie, continuing to run endlessly.
        }
        System.out.println( "App running on main thread ending at " + Instant.now() + "." );
    }

    class Event {
        public UUID id;
        public String name;
        public Instant when;

        public Event ( UUID id , String name , Instant when ) {
            this.id = id;
            this.name = name;
            this.when = when;
        }

        public void update () {
            this.when = Instant.now();
        }
    }
}

运行时。

TRACE - doIt 运行于 2018-09-24T20:16:25.794081-07:00[America/Los_Angeles]

TRACE - 可从 2018-09-25T03:16:25.832025Z 开始运行。 Runnable 在 2018-09-25T03:16:25.832025Z 结束运行。持续时间:PT0.025342S。

TRACE - 可从 2018-09-25T03:18:25.829634Z 开始运行。 Runnable 在 2018-09-25T03:18:25.829634Z 结束运行。持续时间:PT0.001121S。

此屏幕截图中显示的文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 2020-06-02
    相关资源
    最近更新 更多