1.用IDEA创建名叫springboot-file的SpringBoot项目,并将Package name 改为com.example.springboot,导入Spring Web和thymeleaf依赖。(如果创建过程中遇到了问题,可以看我写的文章《IDEA中创建SpringBoot项目,并实现HelloWorld》中前三个步骤。)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.文件上传分为单文件上传和多文件上传,这里通过一个方法实现。在src/main/resources/templates文件下创建upload.html文件。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
</head>
<body>
<form th:action="@{/upload}" method="post" enctype="multipart/form-data">
选择单个文件<input type="file" name="file"><br><br>
选择多个文件<input type="file" name="files" multiple><br><br>
<input type="submit" value="上传">
</form>
</body>
</html>
3.在src/main/resources文件下的application.properties文件中,添加文件保存的路径。
filePath=E:/springboot_save_file/
4.在src/main/resources的com.example.springboot文件夹下,创建包controller,并创建FileController类,用于处理所有的文件上传和下载请求。(由于下载页面还没写,所以上传文件完成后,会报404错误,但是不影响功能)
package com.example.springboot.controller;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
@Controller
public class FileController {
//读取application.properties文件中的filePath属性
@Value("${filePath}")
private String filePath;
/**
* 前往上传页面
* @return 页面名称
*/
@GetMapping({"/upload", ""})
public String goIndex() {
return "upload";
}
/**
* 将文件保存到指定文件夹
* @param file 单个文件
* @param files 多个文件
* @return 重定向到controller层中前往下载页面的url
* @throws IOException
*/
@PostMapping("/upload")
public String uploadAndGoDownLoad(@RequestPart("file") MultipartFile file,
@RequestPart("files") List<MultipartFile> files) throws IOException {
//判断文件夹是否存在,不存在时,创建文件夹
File directoryFile = new File(filePath);
if (!directoryFile.exists()) {
//创建多个文件夹
directoryFile.mkdirs();
}
//判断文件是否为空,不为空时,保存文件
if (!file.isEmpty()) {
saveFile(file);
}
//判断上传文件个数是否为0
if (files.size() > 0) {
for (MultipartFile multipartFile : files) {
if (!multipartFile.isEmpty()) {
saveFile(multipartFile);
}
}
}
return "redirect:/goDownload";
}
/**
* 保存所有的所有上传的文件名称,前往下载页面
* @param model
* @return 页面名称
*/
@GetMapping("/goDownload")
public String goDownload(Model model) {
File file = new File(filePath);
//判断文件夹是否存在
if (file.exists()) {
//获取文件夹下面的所有名称
String[] list = file.list();
model.addAttribute("fileNames", list);
}
return "download";
}
/**
* 保存文件到指定位置
* @param file 需要上传的文件
* @throws IOException
*/
public void saveFile(MultipartFile file) throws IOException {
//获取文件名
String name = file.getOriginalFilename();
file.transferTo(new File(filePath + name));
}
}
注意:
1.前端向后端传递的form表单enctype属性的值必须"multipart/form-data。
2.前后端请求方式要一致,且必须为post。
3.@RequestPart注解最好加上,但是不加也可以。注意前后端参数的绑定,对于注解的使用,可以查看我写的《SpringMVC中Controller层常用注解》。
结果:

5.对于文件上传功能,相对较为简单,这里就不具体说了。下面会具体介绍文件下载,前后端实现方式和注意的地方。文件下载和文件上传是连在一起的,都写在了FileController类中。首先前端写采用最简单的方式,实现文件下载,将后端实现方式讲完后,再讲其他前端实现方式。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<script>
function downloadFileByOpen(fileName) {
window.open("http://localhost:8080/download/hutool?fileName=" + fileName);
}
</script>
</head>
<body>
<h3>后端hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'" th:onclick="downloadFileByOpen([[${fileName}]])">
</li>
</ul>
</body>
</html>
6.后端实现文件下载方式一:hutool方式。在pom.xml文件中导入hutool-extra的依赖,并设置CharacterEncoding为UTF-8。如果不设置CharacterEncoding,会出现中文文件名乱码。
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId>
<version>5.8.8</version>
</dependency>
package com.example.springboot.controller;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
@Controller
public class FileController {
//读取application.properties文件中的filePath属性
@Value("${filePath}")
private String filePath;
/**
* 前往上传页面
* @return 页面名称
*/
@GetMapping({"/upload", ""})
public String goIndex() {
return "upload";
}
/**
* 将文件保存到指定文件夹
* @param file 单个文件
* @param files 多个文件
* @return 重定向到controller层中前往下载页面的url
* @throws IOException
*/
@PostMapping("/upload")
public String uploadAndGoDownLoad(@RequestPart("file") MultipartFile file,
@RequestPart("files") List<MultipartFile> files) throws IOException {
//判断文件夹是否存在,不存在时,创建文件夹
File directoryFile = new File(filePath);
if (!directoryFile.exists()) {
//创建多个文件夹
directoryFile.mkdirs();
}
//判断文件是否为空,不为空时,保存文件
if (!file.isEmpty()) {
saveFile(file);
}
//判断上传文件个数是否为0
if (files.size() > 0) {
for (MultipartFile multipartFile : files) {
if (!multipartFile.isEmpty()) {
saveFile(multipartFile);
}
}
}
return "redirect:/goDownload";
}
/**
* 保存所有的所有上传的文件名称,前往下载页面
* @param model
* @return 页面名称
*/
@GetMapping("/goDownload")
public String goDownload(Model model) {
File file = new File(filePath);
//判断文件夹是否存在
if (file.exists()) {
//获取文件夹下面的所有名称
String[] list = file.list();
model.addAttribute("fileNames", list);
}
return "download";
}
/**
* 使用Hutool实现文件下载
* @param fileName 要下载的文件名
* @param response
*/
@GetMapping("/download/hutool")
@ResponseBody
public void downloadByHutool(@RequestParam(value = "fileName") String fileName,
HttpServletResponse response) {
//防止中文乱码
response.setCharacterEncoding("UTF-8");
ServletUtil.write(response,new File(filePath + fileName));
}
/**
* 保存文件到指定位置
* @param file 需要上传的文件
* @throws IOException
*/
public void saveFile(MultipartFile file) throws IOException {
//获取文件名
String name = file.getOriginalFilename();
file.transferTo(new File(filePath + name));
}
}
7.后端实现方式二:通过查看Hutool源码,自己模仿着源码写的一种方式。将前端download.html文件中,添加方式二的测试。后端添加方式二的实现代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<script>
function downloadFileByOpen(fileName) {
window.open("http://localhost:8080/download/hutool?fileName=" + fileName);
}
function downloadFileByOpenAndSelf(fileName) {
window.open("http://localhost:8080/download/hutool/self?fileName=" + fileName);
}
</script>
</head>
<body>
<h3>后端hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'" th:onclick="downloadFileByOpen([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端模仿hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'" th:onclick="downloadFileByOpenAndSelf([[${fileName}]])">
</li>
</ul>
</body>
</html>
package com.example.springboot.controller;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
@Controller
public class FileController {
//读取application.properties文件中的filePath属性
@Value("${filePath}")
private String filePath;
/**
* 前往上传页面
* @return 页面名称
*/
@GetMapping({"/upload", ""})
public String goIndex() {
return "upload";
}
/**
* 将文件保存到指定文件夹
* @param file 单个文件
* @param files 多个文件
* @return 重定向到controller层中前往下载页面的url
* @throws IOException
*/
@PostMapping("/upload")
public String uploadAndGoDownLoad(@RequestPart("file") MultipartFile file,
@RequestPart("files") List<MultipartFile> files) throws IOException {
//判断文件夹是否存在,不存在时,创建文件夹
File directoryFile = new File(filePath);
if (!directoryFile.exists()) {
//创建多个文件夹
directoryFile.mkdirs();
}
//判断文件是否为空,不为空时,保存文件
if (!file.isEmpty()) {
saveFile(file);
}
//判断上传文件个数是否为0
if (files.size() > 0) {
for (MultipartFile multipartFile : files) {
if (!multipartFile.isEmpty()) {
saveFile(multipartFile);
}
}
}
return "redirect:/goDownload";
}
/**
* 保存所有的所有上传的文件名称,前往下载页面
* @param model
* @return 页面名称
*/
@GetMapping("/goDownload")
public String goDownload(Model model) {
File file = new File(filePath);
//判断文件夹是否存在
if (file.exists()) {
//获取文件夹下面的所有名称
String[] list = file.list();
model.addAttribute("fileNames", list);
}
return "download";
}
/**
* 使用Hutool实现文件下载
* @param fileName 要下载的文件名
* @param response
*/
@GetMapping("/download/hutool")
@ResponseBody
public void downloadByHutool(@RequestParam(value = "fileName") String fileName,
HttpServletResponse response) {
//防止中文乱码
response.setCharacterEncoding("UTF-8");
ServletUtil.write(response,new File(filePath + fileName));
}
/**
* 模仿hutool实现文件下载
* @param fileName 要下载的文件名
* @param response
* @throws IOException
*/
@GetMapping("/download/hutool/self")
@ResponseBody
public void downloadBySelfAndHutool(@RequestParam(value = "fileName") String fileName,
HttpServletResponse response) throws IOException {
//设置字符编码
response.setCharacterEncoding("UTF-8");
//以下模仿hutool进行相应设置
//设置内容类型
response.setHeader("Content-Type", "application/octet-stream");
//设置文件名,是解决中文乱码的关键
response.setHeader("Content-Disposition", String.format("attachment;filename=\"%s\"", URLEncoder.encode(fileName,"UTF-8")));
//将文件取出,并写到response
FileInputStream fileInputStream = new FileInputStream(filePath + fileName);
OutputStream outputStream = response.getOutputStream();
byte[] bytes = new byte[1024];
int length;
while ((length = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, length);
}
fileInputStream.close();
outputStream.flush();
outputStream.close();
}
/**
* 保存文件到指定位置
* @param file 需要上传的文件
* @throws IOException
*/
public void saveFile(MultipartFile file) throws IOException {
//获取文件名
String name = file.getOriginalFilename();
file.transferTo(new File(filePath + name));
}
}
8.上面两种后端实现文件下载方式,对于前端来说,无论是通过open方式,还是通过ajax方式都可以使用。下面这种是通过返回值方式,这种方式主要用于前后端分离项目中。下面以原生的Ajax,模拟前后端分离项目,介绍前后端的实现方式。
download.html中添加Ajax和返回值方式实现文件下载的测试。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<script>
function downloadFileByOpen(fileName) {
window.open("http://localhost:8080/download/hutool?fileName=" + fileName);
}
function downloadFileByOpenAndSelf(fileName) {
window.open("http://localhost:8080/download/hutool/self?fileName=" + fileName);
}
function downloadFileByAjax(fileName) {
let xhr = new XMLHttpRequest;
xhr.open("get","/download/return?fileName=" + fileName,true);
//发送请求
xhr.send();
xhr.responseType="blob";
xhr.onload = function() {
if(this.status === 200) {
let blob = new Blob([this.response]);
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
// 释放URL 对象
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
}
}
}
</script>
</head>
<body>
<h3>后端hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByOpen([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端模仿hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByOpenAndSelf([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端返回值 + 前端Ajax方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByAjax([[${fileName}]])">
</li>
</ul>
</body>
</html>
FileController类中添加返回值方式实现文件上传的代码。(FileController类最终代码)
package com.example.springboot.controller;
import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.List;
@Controller
public class FileController {
//读取application.properties文件中的filePath属性
@Value("${filePath}")
private String filePath;
/**
* 前往上传页面
* @return 页面名称
*/
@GetMapping({"/upload", ""})
public String goIndex() {
return "upload";
}
/**
* 将文件保存到指定文件夹
* @param file 单个文件
* @param files 多个文件
* @return 重定向到controller层中前往下载页面的url
* @throws IOException
*/
@PostMapping("/upload")
public String uploadAndGoDownLoad(@RequestPart("file") MultipartFile file,
@RequestPart("files") List<MultipartFile> files) throws IOException {
//判断文件夹是否存在,不存在时,创建文件夹
File directoryFile = new File(filePath);
if (!directoryFile.exists()) {
//创建多个文件夹
directoryFile.mkdirs();
}
//判断文件是否为空,不为空时,保存文件
if (!file.isEmpty()) {
saveFile(file);
}
//判断上传文件个数是否为0
if (files.size() > 0) {
for (MultipartFile multipartFile : files) {
if (!multipartFile.isEmpty()) {
saveFile(multipartFile);
}
}
}
return "redirect:/goDownload";
}
/**
* 保存所有的所有上传的文件名称,前往下载页面
* @param model
* @return 页面名称
*/
@GetMapping("/goDownload")
public String goDownload(Model model) {
File file = new File(filePath);
//判断文件夹是否存在
if (file.exists()) {
//获取文件夹下面的所有名称
String[] list = file.list();
model.addAttribute("fileNames", list);
}
return "download";
}
/**
* 使用Hutool实现文件下载
* @param fileName 要下载的文件名
* @param response
*/
@GetMapping("/download/hutool")
@ResponseBody
public void downloadByHutool(@RequestParam(value = "fileName") String fileName,
HttpServletResponse response) {
//防止中文乱码
response.setCharacterEncoding("UTF-8");
ServletUtil.write(response,new File(filePath + fileName));
}
/**
* 模仿hutool实现文件下载
* @param fileName 要下载的文件名
* @param response
* @throws IOException
*/
@GetMapping("/download/hutool/self")
@ResponseBody
public void downloadBySelfAndHutool(@RequestParam(value = "fileName") String fileName,
HttpServletResponse response) throws IOException {
//设置字符编码
response.setCharacterEncoding("UTF-8");
//以下模仿hutool进行相应设置
//设置内容类型
response.setHeader("Content-Type", "application/octet-stream");
//设置文件名,是解决中文乱码的关键
response.setHeader("Content-Disposition", String.format("attachment;filename=\"%s\"", URLEncoder.encode(fileName,"UTF-8")));
//将文件取出,并写到response
FileInputStream fileInputStream = new FileInputStream(filePath + fileName);
OutputStream outputStream = response.getOutputStream();
byte[] bytes = new byte[1024];
int length;
while ((length = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, length);
}
fileInputStream.close();
outputStream.flush();
outputStream.close();
}
/**
* 通过返回值方式,实现文件下载
* @param fileName 文件名
* @return 文件流和请求头信息
* @throws IOException
*/
@GetMapping("/download/return")
@ResponseBody
public ResponseEntity<InputStreamResource> download(@RequestParam(value = "fileName") String fileName) throws IOException {
// 读取文件
String path = filePath + fileName;
FileSystemResource file = new FileSystemResource(path);
// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getFilename()));
return ResponseEntity
.ok()
.headers(headers)
.contentLength(file.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(file.getInputStream()));
}
/**
* 保存文件到指定位置
* @param file 需要上传的文件
* @throws IOException
*/
public void saveFile(MultipartFile file) throws IOException {
//获取文件名
String name = file.getOriginalFilename();
file.transferTo(new File(filePath + name));
}
}
注意:
1.前端采用原生的ajax实现的,responseType最好设置为blob(设置为arraybuffer也可以,就是增加转换为blob的步骤)。(开始打算用jQuery中的ajax,但是设置responseType为blob会报错。查找原因,也解决不了。一般这种后端实现方式,对应的是前后端分离项目,然后前端使用的axios。我的能力也有限,知道解决方式的可以评论区留言,一起进步。

报错:jquery.js:10287 Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').)
2.查看后端代码可以发现,这种下载文件的方式,我们不用去考虑中文乱码问题了。
9.前端使用axios,后端继续使用返回值方式,实现文件下载功能。以下是download.html最终代码。后端代码没有改变。这种前后端配合方式实现文件下载,在前后端分离项目中应用广泛(尤其在vue+springboot前后端组合中)。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<!-- 导入axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function downloadFileByOpen(fileName) {
window.open("http://localhost:8080/download/hutool?fileName=" + fileName);
}
function downloadFileByOpenAndSelf(fileName) {
window.open("http://localhost:8080/download/hutool/self?fileName=" + fileName);
}
function downloadFileByAjax(fileName) {
let xhr = new XMLHttpRequest;
xhr.open("get", "/download/return?fileName=" + fileName, true);
//发送请求
xhr.send();
xhr.responseType = "blob";
xhr.onload = function () {
if (this.status === 200) {
let blob = new Blob([this.response]);
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
// 释放URL 对象
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
}
}
}
function downloadFileByAxios(fileName) {
axios({
url: "/download/return",
method: "get",
responseType: "blob",
params: {
fileName
}
}).then(function (res) {
console.log(res);
let blob = new Blob([res.data]);
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
// 释放URL 对象
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
})
}
</script>
</head>
<body>
<h3>后端hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByOpen([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端模仿hutool + 前端open方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByOpenAndSelf([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端返回值 + 前端Ajax方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByAjax([[${fileName}]])">
</li>
</ul>
<br>
<br>
<h3>后端返回值 + 前端axios方式</h3>
<div th:if="${fileNames} == null">没有文件可下载</div>
<ul>
<li th:each="fileName : ${fileNames}" th:text="${fileName} + ' 下载'"
th:onclick="downloadFileByAxios([[${fileName}]])">
</li>
</ul>
</body>
</html>
SpringBoot实现文件上传和下载部分,到此结束了。我也是个新手,上面很多内容不够完善,甚至有些是错误的,请大家见谅。这是我在学习过程中做的笔记,感觉对大家可能有所帮助才发出来的,大家可以选择性查看。我也是在不断学习,不断完善自己。如果我在学习过程中,感觉对大家有用的部分,也会再次分享给大家的。谢谢!
上面有个问题还没有解决,也就是,前端怎么通过JQuery中的Ajax实现文件下载。这种方式和原生Ajax区别不大,下面就是代码,只需要设置返回类型为blob即可。(有时候,一个小问题,如果不熟悉,真的很难解决的。。。)
$.ajax({
type: "get",
url: "/download/return",
data: {
fileName
},
xhrFields: { responseType: "blob" },
success: function (response) {
let blob = new Blob([response]);
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
// 释放URL 对象
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
}
});
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A
我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只