Add support for importing unzipped CSV

This commit is contained in:
Kailash Nadh 2018-11-27 12:21:59 +05:30
parent 8e10bf3b16
commit 8a0a7a195e
2 changed files with 25 additions and 17 deletions

View File

@ -118,7 +118,7 @@ class TheFormDef extends React.PureComponent {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
{...formItemLayout} {...formItemLayout}
label="ZIP file"> label="CSV or ZIP file">
<div className="dropbox"> <div className="dropbox">
{getFieldDecorator("file", { {getFieldDecorator("file", {
valuePropName: "file", valuePropName: "file",
@ -129,11 +129,11 @@ class TheFormDef extends React.PureComponent {
multiple={ false } multiple={ false }
fileList={ this.state.fileList } fileList={ this.state.fileList }
beforeUpload={ this.onFileChange } beforeUpload={ this.onFileChange }
accept=".zip"> accept=".zip,.csv">
<p className="ant-upload-drag-icon"> <p className="ant-upload-drag-icon">
<Icon type="inbox" /> <Icon type="inbox" />
</p> </p>
<p className="ant-upload-text">Click or drag the ZIP file here</p> <p className="ant-upload-text">Click or drag a CSV or ZIP file here</p>
</Upload.Dragger> </Upload.Dragger>
)} )}
</div> </div>
@ -316,8 +316,8 @@ class Import extends React.PureComponent {
<hr /> <hr />
<div className="help"> <div className="help">
<h2>Instructions</h2> <h2>Instructions</h2>
<p>Upload a ZIP file with a single CSV file in it <p>Upload a CSV file or a ZIP file with a single CSV file in it
to bulk import a large number of subscribers in a single shot. to bulk import a subscribers.
</p> </p>
<p> <p>
The CSV file should have the following headers with the exact column names The CSV file should have the following headers with the exact column names

View File

@ -6,6 +6,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"github.com/knadh/listmonk/subimporter" "github.com/knadh/listmonk/subimporter"
"github.com/labstack/echo" "github.com/labstack/echo"
@ -37,8 +38,7 @@ func handleImportSubscribers(c echo.Context) error {
} }
if r.Mode != subimporter.ModeSubscribe && r.Mode != subimporter.ModeBlacklist { if r.Mode != subimporter.ModeSubscribe && r.Mode != subimporter.ModeBlacklist {
return echo.NewHTTPError(http.StatusBadRequest, return echo.NewHTTPError(http.StatusBadRequest, "Invalid `mode`")
"Invalid `mode`")
} }
if len(r.Delim) != 1 { if len(r.Delim) != 1 {
@ -78,17 +78,25 @@ func handleImportSubscribers(c echo.Context) error {
} }
go impSess.Start() go impSess.Start()
// For now, we only extract 1 CSV from the ZIP. Handling async CSV if strings.HasSuffix(strings.ToLower(file.Filename), ".csv") {
// imports is more trouble than it's worth. go impSess.LoadCSV(out.Name(), rune(r.Delim[0]))
dir, files, err := impSess.ExtractZIP(out.Name(), 1) } else {
if err != nil { // Only 1 CSV from the ZIP is considered. If multiple files have
return echo.NewHTTPError(http.StatusInternalServerError, // to be processed, counting the net number of lines (to track progress),
fmt.Sprintf("Error extracting ZIP file: %v", err)) // keeping the global import state (failed / successful) etc. across
} else if len(files) == 0 { // multiple files becomes complex. Instead, it's just easier for the
return echo.NewHTTPError(http.StatusBadRequest, // end user to concat multiple CSVs (if there are multiple in the first)
"No CSV files found to import.") // place and uploada as one in the first place.
dir, files, err := impSess.ExtractZIP(out.Name(), 1)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError,
fmt.Sprintf("Error extracting ZIP file: %v", err))
} else if len(files) == 0 {
return echo.NewHTTPError(http.StatusBadRequest,
"No CSV files found to import.")
}
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
} }
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()}) return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()})
} }