2014-10-01 12 views
25

Próbuję wyeksportować tabelę bazy danych przy użyciu Laravel jako pliku csv. Chciałbym, aby użytkownik mógł wybrać przycisk Export as CSV i pobrać tabelę jako plik csv. Obecnie mam zdobyć ten kod, ale to nie działa:Używanie Laravel do pobierania tabeli jako CSV

mój przycisk:

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

moja droga:

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); 
}); 

Zwraca mi ten błąd:

The file "Resource id #154" does not exist 

I stwierdziłem, że dzieje się tak dlatego, że próbuje pobrać plik, który nie istnieje. Czy istnieje inny sposób, w jaki mogę zmodyfikować mój kod, aby pobrać go jako csv.

+0

Pomyśl o tym, co się stanie, jeśli dwie osoby hit tej trasie w dokładnie w tym samym czasie ... –

+0

Nie wiem? Zgaduję, że coś jest nie tak – Javacadabra

+0

Zobacz moją odpowiedź na temat problemów z aktualnym kodem. –

Odpowiedz

16

prawie wszystko jest w porządku z wyjątkiem tej linii:

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

należy zmienić ta linia do:

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

Twoja odpowiedź działa, ale podejście autora do problemu jest złe; Sugeruję utworzenie CSV w pamięci i obsługiwanie go stamtąd, zamiast tworzenia pliku i wywoływania niepotrzebnych problemów z IO + dyskiem, jeśli dwie osoby natrafią na tę trasę dokładnie w tym samym czasie. –

+2

@ AndréDaniel, masz rację, +1 do tego, ale nie wiemy, co dokładnie dzieje się z kodem (ten kod może być tylko próbką), a rozwiązanie tylko do poprawnego pobrania pliku to jak pokazałem –

+2

@ user2629998 tworzenie csv w pamięci ograniczyłoby rozmiar csv do dostępnej pamięci. Nie sądzę, że moglibyśmy/powinniśmy przyjąć założenie, że plik csv będzie mały. zawsze mógł wygenerować losową nazwę pliku za każdym razem, gdy ktoś trafi na trasę, aby zapobiec problemom z jednoczesnymi połączeniami. –

0

Wszystko wygląda dobrze z wyjątkiem tej linii:

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

$handle nie wskazuje na prawidłową ścieżkę do pliku. Powinna ona być pełna ścieżka do tweets.csv, na przykład:

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

$file gdzie powinno być coś podobnego $file = '/path/to/download/tweets.csv'

13

EDIT: patrz this answer dla lepszego rozwiązania; Zachowam swoją odpowiedź poniżej, ale zauważ, że ma problemy, takie jak brak wartości wymuszających i używanie nierozsądnych ilości pamięci podczas generowania dużych plików.

Niepotrzebnie tworzysz plik na dysku; indukuje to dysk IO i spowoduje problemy, jeśli dwie osoby zażądają tego adresu URL dokładnie w tym samym czasie (dwa wystąpienia struktury zapiszą do tego samego pliku i wystąpią złe rzeczy, takie jak wyświetlanie uszkodzonego pliku lub awarie z wyjątkiem).

Użyj tego zamiast:

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

Pamiętaj, aby zobaczyć odpowiedź Erika poniżej w odniesieniu do tych nieprawidłowo wymykających się wartości, które mogłyby bardzo szybko doprowadzić do nieprawidłowo sformatowanych plików CSV ... oraz problemów z pamięcią w przypadku dużych plików. – Ryan

+1

@Ryan dzięki za sprowadzenie tego; Poprawiłem odpowiedź i dodałem ostrzeżenie na górze oraz link do lepszej odpowiedzi. –

50

Natknąłem się tutaj próbuje sprawdzić, czy laravel coś wbudowany domyślnie - odpowiedź na to pytanie trochę mnie martwić. Zgadzam się z @ andré-daniel, że właściwą metodą jest nie zapisywanie pliku, ale jego implementacja polega na ręcznym zestawianiu wartości, które zawiodłyby, gdyby jakakolwiek wartość zawierała cytaty, spacje itp.

To jest bardziej Rozbudowane rozwiązanie, używając Laravel: Response::stream i php's fputcsv, aby poprawnie sformatować każdą linię (uniknie cytatów i zacytuje niezbędne ciągi).zobacz http://php.net/manual/en/function.fputcsv.php szczegóły)

<?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

Właśnie zauważyłem, że istnieją dwa nagłówki Content-type. Prawidłowy to text/csv, zgodnie z [RFC 4180] (http://tools.ietf.org/html/rfc4180). –

+0

Myślę, że to lepsze rozwiązanie. Nie pozostawi to publicznie pliku "tweets.csv" na twoim serwerze! – Timo002

+1

'-> ToArray()' nie było dla mnie niezbędne w wersji 5 - z jakiegoś powodu złamało nawet foreach - usunęło je - wszystko działa idealnie dobrze;) – DominikAngerer

1

Oto kompletny kod, aby pobrać plik 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

Witam, czy masz pomysł, w jaki sposób mogę eksportować bez podawania nazw kolumn? Podczas eksportowania do pliku csv podaję również nazwy kolumn, ale nie chcę ich uwzględniać, ponieważ wstawiam do nich niestandardowe nagłówki. – Pedro

0

spróbować

$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'); 
Powiązane problemy