Headers already sent on custom plugin (Export function)

You are probably executing your process_bulk_action function in template where some html and headers are already sent. You have to use handle_bulk_actions-edit-post hook to execute your code before any content is sent. This might look something like that:

function wpse_287406_register_export_option($bulk_actions) {

    $bulk_actions['export_csv'] = __( 'Export to CSV');

    return $bulk_actions;
}

add_filter( 'bulk_actions-edit-post', 'wpse_287406_register_export_option' );

function wpse_287406_export_csv( $redirect_to, $doaction, $post_ids ) {

    if ( $doaction !== 'export_csv' ) {

        return $redirect_to;
    }

    // Export CSV code

    $file = realpath( dirname( __FILE__ ) . '/file.csv' );

    header( 'Content-Description: File Transfer' );
    header( 'Content-Type: application/octet-stream' );
    header( 'Content-Disposition: attachment; filename=file.csv' );
    header( 'Content-Transfer-Encoding: binary' );
    header( 'Expires: 0' );
    header( 'Cache-Control: must-revalidate' );
    header( 'Pragma: public' );
    header( 'Content-Length: ' . filesize($file) );

    readfile($file);

    exit;
}

add_action( 'handle_bulk_actions-edit-post', 'wpse_287406_export_csv', 10, 3 );