1, Introduction to Apache POI
Excel processing is often designed in development, such as exporting excel and importing excel into the database. A few years ago, there were two frameworks commonly used to operate excel, one is apache poi and the other is Java Excel. However, now there are EasyPoi and Alibaba open source EasyExcel, which will be sorted out later
-
Apache POI is a free, open source, cross platform Java API written in Java. Apache POI provides Java programs with the ability to read and write files in Microsoft Office (Excel, WORD, PowerPoint, Visio, etc.). POI is the acronym of "Poor Obfuscation Implementation", which means "poor fuzzy implementation".
Official documents: http://poi.apache.org/apidocs/index.html
-
Java Excel is an open source project. Through it, Java developers can read the contents of Excel files, create new Excel files, and update existing Excel files. jxl has gradually replaced POI excel because of its small and easy-to-use characteristics, and has become the first choice for more and more Java developers to generate Excel files.
2: Classes commonly used by Apache POI
- HSSF - provides the function of reading and writing files in Microsoft Excel XLS format. [version 03]
- XSSF - provides the function of reading and writing Microsoft Excel OOXML XLSX format files. [version 07]
- HWPF - provides the function of reading and writing files in Microsoft Word DOC97 format.
- XWPF - provides the function of reading and writing files in Microsoft Word DOC2003 format.
- HSLF - provides the function of reading and writing files in Microsoft PowerPoint format.
- HDGF - provides the ability to read files in Microsoft Visio format.
- HPBF - provides the function of reading files in Microsoft Publisher format.
- HSMF - provides the ability to read files in Microsoft Outlook format.
Common classes and methods
HSSFWorkbook: a workbook that represents the entire document of an excel
- HSSFWorkbook(); // Create a new workbook
- HSSFWorkbook(InputStream inputStream); // Create a workbook associated with the input stream. You can package an excel file into a workbook
- HSSFSheet createSheet(String sheetname); Create a new Sheet
- HSSFSheet getSheet(String sheetName); Get Sheet by name
- HSSFSheet getSheetAt(int index); // Get the Sheet through the index. The index starts from 0
- HSSFCellStyle createCellStyle(); Create cell style
- int getNumberOfSheets(); Get the number of sheet s
- setActiveSheet(int index); Set the default selected worksheet
- write(); // Write data
- write(File newFile); // Write data to the specified file
- write(OutputStream stream);
HSSFSheet: worksheet
- HSSFRow createRow(int rownum); To create a new line, you need to specify the line number, which starts from 0
- HSSFRow getRow(int index); Gets the specified row based on the index
- int addMergedRegion(CellRangeAddress region); merge cell
- CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol); Cell range is used to merge cells. You need to specify the first row, last row, first column and last column to be merged.
- autoSizeColumn(int column); Automatically adjust the width of the column to fit the content
- getLastRowNum(); Get the index of the last row. If there is no row or only one row, return 0
- setColumnWidth(int columnIndex, int width); Set the width of a column. Width = number of characters * 256. For example, the width of 20 characters is 20 * 256
HSSFRow: row
- HSSFCell createCell(int column); Create a new cell
- HSSFCell setCell(shot index);
- HSSFCell getCell(shot index);
- setRowStyle(HSSFCellStyle style); Set line style
- short getLastCellNum(); Get the last cell number. If the cell has the first start, lastCellNum is the number of columns
- setHeightInPoints(float height); Sets the height of the row
HSSFCell: cell
- setCellValue(String value); Sets the value of the cell
- setCellType(); Set the cell type, such as string, number, Boolean, etc
- setCellStyle(); Set cell style
- String getStringCellValue(); Gets the string value in the cell
- setCellStyle(HSSFCellStyle style); Set cell styles, such as font, bold, and formatting
- setCellFormula(String formula); The calculation formula is set, and the calculation result is used as the value of the cell. It also provides functions commonly used in exceptions, such as sum(A1,C1), date function, string correlation function, CountIf and SumIf functions, random number function, etc
- HSSFCellStyle: cell style
- setFont(Font font); Set font style for cells
- setAlignment(HorizontalAlignment align); // Set horizontal alignment
- setVerticalAlignment(VerticalAlignment align); // Set vertical alignment
- setFillPattern(FillPatternType fp);
- setFillForegroundColor(short bg); set foreground color
- setFillBackgroundColor(short bg); Set background color
HSSFFont: font,
- setColor(short color); // Font color settings
- setBold(boolean bold); // Set whether bold
- setItalic(boolean italic); Set tilt
- setUnderline(byte underline); Set underline
HSSFName: name
- HSSFDataFormat: date formatting
- HSSFHeader: the header of the Sheet
- Hssffolder: the tail of the Sheet
- HSSFDateUtil: Date tool
- HSSFPrintSetup: Print Setup
- HSSFErrorConstants: error information table
Relationships in workbooks, worksheets, rows and cells in Excel:
An Excel file corresponds to a Workbook (HSSF Workbook),
A workbook can be composed of multiple sheets (hssfsheets), and a sheet is composed of multiple rows (hssfrows),
A row is composed of multiple cells (hssfcells)
3, Example demonstration
1. Create a normal maven project
2. Introduce the dependency of Apache poi
<!--xLs(03 edition)--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version >3.9</version> </dependency> <!--xLsx(07 edition)--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.9</version> </dependency>
Example 1: generate an Excel file [version 03] on the desktop
@Test public void createExcel() throws IOException{ // Get desktop path FileSystemView fsv = FileSystemView.getFileSystemView(); String desktop = fsv.getHomeDirectory().getPath(); System.out.println("Desktop path:"+desktop); String filePath = desktop + "/test.xls"; File file = new File(filePath); OutputStream outputStream = new FileOutputStream(file); //1. Create a workbook HSSFWorkbook workbook = new HSSFWorkbook(); //2. Create a worksheet called test1 HSSFSheet sheet = workbook.createSheet("test1"); //3. Create the first row and set the value HSSFRow row = sheet.createRow(0); row.createCell(0).setCellValue("id"); row.createCell(1).setCellValue("order number"); row.createCell(2).setCellValue("Order time "); row.createCell(3).setCellValue("number"); row.createCell(4).setCellValue("Unit Price"); row.createCell(5).setCellValue("Order amount"); row.setHeightInPoints(30); // Sets the height of the row //4. Create the second row and set the value HSSFRow row1 = sheet.createRow(1); row1.createCell(0).setCellValue("1"); row1.createCell(1).setCellValue("NO00001"); //4.1 set the value of the third column and format the date HSSFCellStyle cellStyle2 = workbook.createCellStyle(); HSSFCreationHelper creationHelper = workbook.getCreationHelper(); cellStyle2.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss")); sheet.setColumnWidth(2, 20 * 256); // Sets the width of the column HSSFCell cell2 = row1.createCell(2); cell2.setCellStyle(cellStyle2); cell2.setCellValue(new Date()); //4.2 setting the fourth column value row1.createCell(3).setCellValue(2); //4.3 set the value of the fifth column and keep two decimal places HSSFCellStyle cellStyle3 = workbook.createCellStyle(); cellStyle3.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00")); HSSFCell cell4 = row1.createCell(4); cell4.setCellStyle(cellStyle3); cell4.setCellValue(29.5); //4.3 setting the sixth column value currency format HSSFCellStyle cellStyle4 = workbook.createCellStyle(); HSSFFont font = workbook.createFont(); font.setFontName("Chinese block letters"); font.setFontHeightInPoints((short)15); font.setColor(HSSFColor.RED.index); cellStyle4.setFont(font); HSSFCell cell5 = row1.createCell(5); cell5.setCellFormula("D2*E2"); // Set calculation formula //4.3.1 get the value of calculation formula HSSFFormulaEvaluator e = new HSSFFormulaEvaluator(workbook); cell5 = e.evaluateInCell(cell5); System.out.println("The calculated value is:"+cell5.getNumericCellValue()); //The first worksheet is selected by default workbook.setActiveSheet(0); //Write data to file workbook.write(outputStream); //Close flow outputStream.close(); }
Operation results:
The files generated by the desktop are:
Note: the difference between version 07 and 03 is:
- Version 07 uses XSSFWorkbook and version 03 uses HSSFWorkbook
- The file suffix of version 07 is xlsx, the file suffix of version 03 is xls
- Excel generated in version 07 theoretically has infinite rows, while version 03 has only 65536 rows
The reading and writing operations are basically the same, except that the object and suffix are different
Example 2: a large amount of data in version 03 is written into [HSSF]
- Disadvantages: only 65536 lines can be processed at most, otherwise an exception will be reported
java.lang.IllegalArgumentException:Invalid row number (65536) outside allowable range (0.. 65535)
- Advantages: write cache in the process, do not operate the disk, and write to the disk at the last time, which is fast
@Test public void testwrite03BigData() throws IOException { //time long begin = System.currentTimeMillis(); //Create a thin Workbook workbook = new HSSFWorkbook(); //Create table Sheet sheet = workbook.createSheet(); //Write data for (int rowNum = 0; rowNum < 65536; rowNum++) { Row row = sheet.createRow(rowNum); for (int cellNum = 0; cellNum < 10; cellNum++) { Cell cell = row.createCell(cellNum); cell.setCellValue(cellNum); } } FileOutputStream fos = new FileOutputStream(PATH + "03 edition Excel Massive data test.xls"); workbook.write(fos); fos.close(); System.out.println("over"); long end = System.currentTimeMillis(); System.out.println((double) (end - begin) / 1000); }
Operation results:
Example 3: a large amount of data in version 07 is written into [XSSF]
- Disadvantages: when writing data, the speed is very slow, which consumes a lot of memory, and memory overflow will also occur, such as 1 million pieces of data
- Advantages: it can write a large amount of data, such as 200000 pieces of data
@Test public void testwrite07BigData() throws IOException { //time long begin = System.currentTimeMillis(); //Create a thin Workbook workbook = new XSSFWorkbook(); //Create table Sheet sheet = workbook.createSheet(); //Write data for (int rowNum = 0; rowNum < 65536; rowNum++) { Row row = sheet.createRow(rowNum); for (int cellNum = 0; cellNum < 10; cellNum++) { Cell cell = row.createCell(cellNum); cell.setCellValue(cellNum); } } FileOutputStream fos = new FileOutputStream(PATH + "07 edition Excel Massive data test.xlsx"); workbook.write(fos); fos.close(); System.out.println("over"); long end = System.currentTimeMillis(); System.out.println((double) (end - begin) / 1000); }
Operation results:
It can be found that writing data in version 07 is very slow, consumes a lot of memory and takes longer than that in version 03. Next, we will introduce another way to write big data, that is SXSSF
Example 4: write a large amount of data into [SXSSF]
Advantages: it can write a large number of databases, such as 1 million or more, with fast data writing speed and less memory
be careful:
- Temporary files will be generated during the process and need to be cleaned up
- By default, 100 records are saved in memory. If this number is exceeded, the first data is written to the temporary file
- If you want to customize the amount of data in memory, you can use new SXSSFWorkbook
@Test public void testwrite07_S_BigData() throws IOException { //time long begin = System.currentTimeMillis(); //Create a thin Workbook workbook = new SXSSFWorkbook(100); //Create table Sheet sheet = workbook.createSheet(); //Write data for (int rowNum = 0; rowNum < 65536; rowNum++) { Row row = sheet.createRow(rowNum); for (int cellNum = 0; cellNum < 10; cellNum++) { Cell cell = row.createCell(cellNum); cell.setCellValue(cellNum); } } FileOutputStream fos = new FileOutputStream(PATH + "07_S_edition Excel Massive data test.xlsx"); workbook.write(fos); fos.close(); //Clear temporary cache ((SXSSFWorkbook)workbook).dispose(); System.out.println("over"); long end = System.currentTimeMillis(); System.out.println("Time consuming:"+(double) (end - begin) / 1000+"second"); }
Operation result: the speed is greatly improved
SXSSWorkbook comes from the official explanation: Implementation: the streaming xssf workbook version of BigGridDemo policy. This allows very large files to be written without running out of memory, because only configurable line portions are saved in memory at any time.
Example 5: reading Excel and parsing data [multi data type verification]
The data in the table are as follows:
The characteristics of the table are: the data in the table includes string, value, date, Boolean value and null value. Therefore, when reading, it is necessary to judge the type of the field value in the table, and then analyze it, otherwise an error will be reported:
@Test public void testCellType() throws Exception { //Get file stream FileInputStream fis = new FileInputStream(PATH +"Multi type test table.xlsx"); //Create a workbook. He can operate everything he can operate with excel Workbook workbook = new XSSFWorkbook(fis); Sheet sheet = workbook.getSheetAt(0); //Get title content Row rowTitle = sheet.getRow(0); if (rowTitle != null) { //How many columns of data are there in a row int cellCount = rowTitle.getPhysicalNumberOfCells(); for (int cellNum = 0; cellNum < cellCount; cellNum++) { Cell cell = rowTitle.getCell(cellNum); if (cell != null) { int cellType = cell.getCellType(); String cellValue = cell.getStringCellValue(); System.out.print(cellValue + "|"); } } System.out.println(); } //Get the contents of the table //Gets how many rows in the table have data int rowCount = sheet.getPhysicalNumberOfRows(); for (int rowNum = 1; rowNum < rowCount; rowNum++) { Row rowData = sheet.getRow(rowNum); if (rowData != null) { //Read column int cellCount = rowTitle.getPhysicalNumberOfCells(); for (int cellNum = 0; cellNum < cellCount; cellNum++) { System.out.println("[" + (rowNum + 1) + "-" + (cellNum + 1) + "]"); Cell cell = rowData.getCell(cellNum); //Data type of matching column if (cell != null) { int cellType = cell.getCellType(); String cellValue = ""; switch (cellType) { case HSSFCell.CELL_TYPE_STRING://character System.out.print("[ String]"); cellValue = cell.getStringCellValue(); break; case HSSFCell.CELL_TYPE_BOOLEAN://Boolean System.out.print("[ BOOLEAN]"); cellValue = String.valueOf(cell.getBooleanCellValue()); break; case HSSFCell.CELL_TYPE_BLANK://empty System.out.print("[ BLANK]"); break; case HSSFCell.CELL_TYPE_NUMERIC://Number (date, ordinary number) System.out.print("[ NUMERIC]"); if (HSSFDateUtil.isCellDateFormatted(cell)) {// date System.out.print("--[[date]"); Date date = cell.getDateCellValue(); cellValue = new DateTime(date).toString("yyyy-MM-dd HH:mm:ss"); } else { //It is not a date format, to prevent the number from being too long! System.out.print("--[Convert to string output]"); cell.setCellType(HSSFCell.CELL_TYPE_STRING); cellValue = cell.toString(); } break; case HSSFCell.CELL_TYPE_ERROR://error System.out.print("[ [wrong data type]"); break; } System.out.println(cellValue); } } } } //Close flow fis.close(); }
Operation results:
4: Exporting and importing Excel from Java Web
1. Export example
I need to export the information in the list below to Excel
controller layer:
@RequestMapping(value = "/exportSearchEcomizeActivityList", method = RequestMethod.GET) private void exportSearchEcomizeActivityList(HttpServletResponse response){ adminService.exportSearchEcomizeActivityList(response); }
Implementation class:
@Override public void exportSearchEcomizeActivityList(HttpServletResponse response) { ServletOutputStream outputStream = null; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try{ // Processing date: 2019-12-03 00:00:00 ~ 2019-12-11 00:00:00 String startDate =""; String endDate=""; String selectDate = request.getParameter("selectDate"); String activityName = request.getParameter("activityName"); if(selectDate != null && !"".equals(selectDate) && selectDate.indexOf("~") !=-1){ String[] splitDate = selectDate.split("~"); startDate = splitDate[0].trim(); endDate = splitDate[1].trim(); } //Get activity information List<EcomizeDateInfoPojo> activityList = ecomizeActivityMapper.findEcomizeActivityByListForExport(activityName); if(CollectionUtils.isNotEmpty(activityList)){ for (EcomizeDateInfoPojo activity : activityList) { // Classification name List<EcomizeActivityTypeModel> typeList = ecomizeActivityMapper.findActivityTypeByActivity(activity.getId()); if(CollectionUtils.isNotEmpty(typeList) && typeList != null){ String typeName = ""; for (EcomizeActivityTypeModel type : typeList) { if(type.getName() != null){ typeName += type.getName() +"/"; } } activity.setType_name(typeName.substring(0,typeName.length() - 1)); } // Number of participants int takeNumber = ecomizeActivityMapper.findEcomizeActivityTakeNumber(activity.getId(),startDate,endDate); activity.setTakeNumber(takeNumber); // Number of orders submitted int putNumber = ecomizeActivityMapper.findEcomizeActivityPutNumber(activity.getId(),startDate,endDate); activity.setPutNumber(putNumber); // Number of new recruits int helpNumber = ecomizeActivityMapper.findEcomizeActivityHelpNumber(activity.getId(),startDate,endDate); activity.setHelpNumber(helpNumber); // Help person times int newNumber = ecomizeActivityMapper.findEcomizeActivityNewNumber(activity.getId(),startDate,endDate); activity.setNewNumber(newNumber); } } //Create poi export data object SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(); //Create sheet page SXSSFSheet sheet = (SXSSFSheet) sxssfWorkbook.createSheet("Export test"); //Create header SXSSFRow headRow = (SXSSFRow) sheet.createRow(0); //Set header information headRow.createCell(0).setCellValue("activity id"); headRow.createCell(1).setCellValue("Activity name"); headRow.createCell(2).setCellValue("Activity classification"); headRow.createCell(3).setCellValue("New activity time"); headRow.createCell(4).setCellValue("Number of participants"); headRow.createCell(5).setCellValue("Number of orders submitted"); headRow.createCell(6).setCellValue("Number of new recruits"); headRow.createCell(7).setCellValue("Help person times"); activityList.forEach(obj -> { //Fill data SXSSFRow dataRow = (SXSSFRow) sheet.createRow(sheet.getLastRowNum() + 1); //Activity id if (obj.getId() != null) { dataRow.createCell(0).setCellValue(obj.getId().toString()); } else { dataRow.createCell(0).setCellValue(""); } //Activity name nickname if (obj.getName() != null) { dataRow.createCell(1).setCellValue(obj.getName()); } else { dataRow.createCell(1).setCellValue(""); } // Activity classification if (obj.getType_name() != null) { dataRow.createCell(2).setCellValue(obj.getType_name()); } else { dataRow.createCell(2).setCellValue(""); } // New activity time if (obj.getCreate_time() != null) { dataRow.createCell(3).setCellValue(simpleDateFormat.format(obj.getCreate_time())); } else { dataRow.createCell(3).setCellValue(""); } // Number of participants if (obj.getTakeNumber() != null) { dataRow.createCell(4).setCellValue(obj.getTakeNumber().toString()); } else { dataRow.createCell(4).setCellValue(""); } // Number of orders submitted if (obj.getPutNumber() != null) { dataRow.createCell(5).setCellValue(obj.getPutNumber().toString()); } else { dataRow.createCell(5).setCellValue(""); } // Number of new recruits if (obj.getNewNumber() != null) { dataRow.createCell(6).setCellValue(obj.getNewNumber().toString()); } else { dataRow.createCell(6).setCellValue(""); } // Help person times if (obj.getHelpNumber() != null) { dataRow.createCell(7).setCellValue(obj.getHelpNumber().toString()); } else { dataRow.createCell(7).setCellValue(""); } }); //Download export String fileName = "Export test"; // Set header information this.request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.ms-excel"); //Be sure to set it to xlsx format response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName .getBytes("gb2312"), "ISO-8859-1") + ".xlsx"); //Create an output stream outputStream = response.getOutputStream(); //Write data sxssfWorkbook.write(outputStream); } catch (Exception e) { e.printStackTrace(); } finally { try { if (outputStream != null) { outputStream.flush(); outputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } }
front end:
1. Bind Onclick event:
2. Monitor and search
3.searchDownload() method
Export test:
2. Import case
In fact, import is the reverse of export. The basic operation of import is to read and analyze the data inside according to the imported file [there is a case on how to analyze Excel data], and then process the parsed data according to your business needs after getting the parsed data. It is not shown in detail here
3. Download Excel template
The essence is to export. The basic operation is the same as that of export, except that the downloaded template has only the header and no other data
controller layer:
@GetMapping("/downloadBonusExcel") public void downloadBonusExcel(HttpServletRequest request, HttpServletResponse response) throws IOException { managerService.downloadBonusExcel(request,response); }
Implementation class:
@Override public void downloadBonusExcel(HttpServletRequest request, HttpServletResponse response) throws IOException { // file name String fileName = "Download template test"; ServletOutputStream outputStream = null; try { //Create poi export data object SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(); //Create sheet page SXSSFSheet sheet =sxssfWorkbook.createSheet(); //Create header SXSSFRow headRow =sheet.createRow(0); //Set header information headRow.createCell(0).setCellValue("Bonus amount"); headRow.createCell(1).setCellValue("cell-phone number"); headRow.createCell(2).setCellValue("remarks"); // Set header information request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("application/vnd.ms-excel"); //Be sure to set it to xlsx format response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("gb2312"), "ISO-8859-1") + ".xlsx"); //Create an output stream outputStream = response.getOutputStream(); //Write data sxssfWorkbook.write(outputStream); } catch (Exception e) { e.printStackTrace(); }finally { outputStream.close(); } }
explain:
- ServletOutputStream class: write binary data into the response stream. In this case, you need to get the input stream, which can be obtained in this way; ServletOutputStream out=response.getOutputStream(); Of course, other output streams can also be used, such as:
File file = new File(filePath); //In this way, outputStream can also be obtained OutputStream outputStream = new FileOutputStream(file);
-
Export file / download file setting content type
-
1. Export to Excel: set the content type to application / vnd ms-Excel
The specific use is as follows:
response.setContentType("application/vnd.ms-excel"); -
2. If you want to provide the open / save dialog box: you need to set the content disposition parameter:
- Attachment - download as attachment
- inline - open online
The specific use is as follows:
response.setHeader("Content-Disposition", "attachment;fileName=" + new String(fileName.getBytes("gb2312"), "ISO-8859-1") + ".xlsx");
Note: SXSSFWorkbook object is used in the above example code, so the file suffix can be set to Xlsx and xls, if it is xssf workbook, it must be xlsx
-
Here are other common file formats:
- 'doc' => 'application/msword',
-
'bin' => 'application/octet-stream',
-
'exe' => 'application/octet-stream',
-
'so' => 'application/octet-stream',
-
'dll' => 'application/octet-stream',
- 'pdf' => 'application/pdf',
-
'ai' => 'application/postscript',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
-
'dir' => 'application/x-director',
-
'js' => 'application/x-javascript',
-
'swf' => 'application/x-shockwave-flash',
-
'xhtml' => 'application/xhtml+xml',
-
'xht' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
-
'mid' => 'audio/midi',
-
'midi' => 'audio/midi',
- 'mp3' => 'audio/mpeg',
-
'rm' => 'audio/x-pn-realaudio',
-
'rpm' => 'audio/x-pn-realaudio-plugin',
-
'wav' => 'audio/x-wav',
-
'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'png' => 'image/png',
-
'css' => 'text/css',
-
'html' => 'text/html',
-
'htm' => 'text/html',
- 'txt' => 'text/plain',
-
'xsl' => 'text/xml',
-
'xml' => 'text/xml',
-
'mpeg' => 'video/mpeg',
-
'mpg' => 'video/mpeg',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie',