2014-10-01 3 views
25

Я пытаюсь экспортировать таблицу базы данных, используя Laravel в качестве файла csv. Я хотел бы, чтобы пользователь мог выбрать кнопку Export as CSV и загрузить таблицу как файл csv. В настоящее время я получил этот код, но он не работает:Использовать Laravel для загрузки таблицы как CSV

моя кнопка:

<a href="/all-tweets-csv" class="btn btn-primary">Export as CSV</a> 

мой маршрут:

Route::get('/all-tweets-csv', function(){ 

    $table = Tweet::all(); 
    $filename = "tweets.csv"; 
    $handle = fopen($filename, 'w+'); 
    fputcsv($handle, array('tweet text', 'screen name', 'name', 'created at')); 

    foreach($table as $row) { 
     fputcsv($handle, array($row['tweet_text'], $row['screen_name'], $row['name'], $row['created_at'])); 
    } 

    fclose($handle); 

    $headers = array(
     'Content-Type' => 'text/csv', 
    ); 

    return Response::download($handle, 'tweets.csv', $headers); 
}); 

возвращает мне эту ошибку:

The file "Resource id #154" does not exist 

И я понял, что это потому, что он пытается загрузить файл, который не существует. Есть ли альтернативный способ, по которому я могу изменить свой код, чтобы загрузить его как csv.

+0

Подумайте о том, что произойдет, если два человека ударил по этому маршруту в точно такое же время ... –

+0

Не знаю? Я угадываю что-то плохое – Javacadabra

+0

См. Мой ответ о том, какие проблемы имеет ваш текущий код. –

ответ

16

Почти все отлично, кроме этой линии:

return Response::download($handle, 'tweets.csv', $headers); 

Вы должны изменить эта строка:

return Response::download($filename, 'tweets.csv', $headers); 
+4

Ваш ответ работает, но авторский подход к проблеме плохой; Я предлагаю создавать csv в памяти и обслуживать его оттуда, а не создавать файл и вызывать ненужные дисковые ошибки ввода-вывода +, если два человека попадают по этому маршруту в одно и то же время. –

+2

@ AndréDaniel, вы правы, +1 для этого, но мы не знаем, что именно происходит с кодом (этот код может быть просто образцом), а решение для загрузки файла правильно, так как я показал –

+2

@ user2629998, создавая csv в памяти ограничит размер csv доступной памяти. Не думайте, что мы могли/должны были предположить, что файл csv будет небольшим. он всегда мог генерировать случайное имя файла каждый раз, когда кто-то попадает на маршрут, чтобы предотвратить проблемы с одновременными вызовами. –

0

Все выглядит хорошо, кроме этой линии:

return Response::download($handle, 'tweets.csv', $headers); 

$handle не указывает на правильный путь к файлу. Он должен быть полный путь к tweets.csv, например:

return Response::download($file, 'tweets.csv', $headers); 

$file, где должно быть что-то вроде $file = '/path/to/download/tweets.csv'

13

EDIT: см. this answer для лучшего решения; Я сохраню свой ответ ниже, но заметьте, что у него есть такие проблемы, как не ускользание значений и использование необоснованных объемов памяти при генерации больших файлов.

Вам необязательно создавать файл на диске; который вызывает диск IO и вызывает проблемы, если два человека запросят этот URL-адрес в одно и то же время (два экземпляра фреймворка будут писать в тот же файл, и произойдет плохой материал, например, служение поврежденному файлу или сбой с исключением).

Используйте вместо этого:

Route::get('/all-tweets-csv', function() { 
    $tweets = Tweets::all(); 

    // the csv file with the first row 
    $output = implode(",", array('tweet text', 'screen name', 'name', 'created at')); 

    foreach ($tweets as $row) { 
     // iterate over each tweet and add it to the csv 
     $output .= implode(",", array($row['tweet_text'], $row['screen_name'], $row['name'], $row['created_at'])); // append each row 
    } 

    // headers used to make the file "downloadable", we set them manually 
    // since we can't use Laravel's Response::download() function 
    $headers = array(
     'Content-Type' => 'text/csv', 
     'Content-Disposition' => 'attachment; filename="tweets.csv"', 
     ); 

    // our response, this will be equivalent to your download() but 
    // without using a local file 
    return Response::make(rtrim($output, "\n"), 200, $headers); 
}); 
+0

Не забудьте увидеть ответ Эрика ниже в отношении того, что это не позволяет избежать ошибок, которые могут привести к неправильной форматировке CSV очень быстро ... а также проблемы с памятью при работе с большими файлами. – Ryan

+1

@ Ryan благодарит за это; Я отредактировал свой ответ и добавил предупреждение наверху и ссылку на лучший ответ. –

50

я наткнулся здесь, пытаясь увидеть, если что-то Laravel встроенный в по умолчанию - ответы на этот вопрос волнует меня немного. Я согласен с @ andré-daniel в том, что правильный метод состоит в том, чтобы не писать файл в первую очередь, но его реализация вручную объединяет значения, которые потерпят неудачу, если какое-либо значение содержало цитаты, пробелы и т. Д.

Это больше надежное решение, используя Laravel's Response::stream и php's fputcsv для правильной форматирования каждой строки (выйдут кавычки и процитируем необходимые строки.см http://php.net/manual/en/function.fputcsv.php подробности)

<?php 

public function download() 
{ 
    $headers = [ 
      'Cache-Control'  => 'must-revalidate, post-check=0, pre-check=0' 
     , 'Content-type'  => 'text/csv' 
     , 'Content-Disposition' => 'attachment; filename=galleries.csv' 
     , 'Expires'    => '0' 
     , 'Pragma'    => 'public' 
    ]; 

    $list = User::all()->toArray(); 

    # add headers for each column in the CSV download 
    array_unshift($list, array_keys($list[0])); 

    $callback = function() use ($list) 
    { 
     $FH = fopen('php://output', 'w'); 
     foreach ($list as $row) { 
      fputcsv($FH, $row); 
     } 
     fclose($FH); 
    }; 

    return Response::stream($callback, 200, $headers); 
} 
+1

Только что заметили два заголовка Content-type. Правильным является текст/csv, согласно [RFC 4180] (http://tools.ietf.org/html/rfc4180). –

+0

Я думаю, что это лучшее решение. Это не оставляет файл 'tweets.csv' общедоступным на вашем сервере! – Timo002

+1

'-> toArray()' не был нужен для laravel 5 для меня - он даже сломал foreach по какой-то причине - удалил его - все отлично работает;) – DominikAngerer

1

Вот полный код для загрузки CSV

$headers = array(
    'Content-Type'  => 'application/vnd.ms-excel; charset=utf-8', 
    'Cache-Control'  => 'must-revalidate, post-check=0, pre-check=0', 
    'Content-Disposition' => 'attachment; filename=abc.csv', 
    'Expires'    => '0', 
    'Pragma'    => 'public', 
); 
$filename = "doenload.csv"; 
$handle = fopen($filename, 'w'); 
fputcsv($handle, [ 
    "id", 
    "name", 
}); 
DB::table("tablename")->chunk(100, function($data) use($handle) { 
     foreach ($data as $row) { 
      // Add a new row with data 
      fputcsv($handle, [ 
       $row->id, 
       $row->name, 
      }); 
fclose($handle); 
return Response::download($filename, "download.csv", $headers); 
0
public function export() 
{ 
    $people = Person::all(); 

    $csv = \League\Csv\Writer::createFromFileObject(new \SplTempFileObject()); 

    $csv->insertOne(\Schema::getColumnListing('people')); 

    foreach ($people as $person) { 
     $csv->insertOne($person->toArray()); 
    } 

    $csv->output('people.csv'); 
} 
+0

Привет, у вас есть идея, как я могу экспортировать без ввода имен столбцов? Когда я экспортирую в csv, я даю мне также имена столбцов, но я не хочу включать, поскольку im вставляет в него некоторые пользовательские заголовки. – Pedro

0

попробовать это

$file_name = "abc"; 
    $postStudent = Input::all(); 
    $ck = DB::table('loan_tags')->select('LAN')->where('liabilitiesId', $postStudent['id'])->get(); 
    $i = 0; 
    foreach ($ck as $row) { 

        $apps[$i]['LAN'] = $row->LAN; 
       $apps[$i]['Account_number'] = $postStudent['account_number']; 
       $apps[$i]['Bank_Name'] = $postStudent['bank_name']; 
       $i++; 
    } 

    ob_end_clean(); 
    ob_start(); 
    Excel::create($file_name, function($excel) use($apps){ 
      $excel->sheet('Sheetname', function($sheet) use($apps){ 

       $sheet->row(1, array(
        'LAN', 'Account number' , 'Bank Name' 
       )); 
       $k = 2; 
       foreach ($apps as $deta) { 
        $sheet->row($k, array($deta['LAN'], $deta['Account_number'], $deta['Bank_Name'] 
        )); 
        $k++; 
       } 
      }); 
     })->download('xlsx'); 
Смежные вопросы