JavaRush /Блоги Java /Random-TG /Захираи файлҳо дар барнома ва маълумот дар бораи онҳо дар...

Захираи файлҳо дар барнома ва маълумот дар бораи онҳо дар пойгоҳи додаҳо

Дар гурӯҳ нашр шудааст
Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 1 Биёед тасаввур кунем, ки шумо дар болои барномаи веби худ кор карда истодаед. Дар мақолаҳои худ ман ба қисмҳои алоҳидаи ин мозаика назар мекунам, масалан: Ин мавзӯъҳо чӣ гуна муфиданд? Ва он, ки ин мисолҳо ба кор дар лоиҳаҳои воқеӣ хеле наздиканд ва санҷиши ин мавзӯъҳо барои шумо хеле муфид хоҳад буд. Имрӯз мо қисмати навбатии ин мозаикаро мегирем - кор бо файлҳо, зеро имрӯз шумо дигар сайтеро пайдо карда наметавонед, ки бо онҳо кор накунад (масалан, ҳама гуна мағозаҳои интернетӣ, шабакаҳои иҷтимоӣ ва ғайра). Баррасӣ бо истифода аз усулҳои боргирӣ/зеркашӣ/нест кардан ҳамчун намуна анҷом дода мешавад ; мо онро дар ҷузвдон (дар захира) дар замимаи худ нигоҳ медорем, то он мушкил нагардад. Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар пойгоҳи додаҳо - 2Идеяи он аст, ки мо ба гайр аз худи файл дар анбори ба истилох файли худ, як an objectеро бо маълумот дар бораи файли мо (андоза, ном ва гайра) дар базаи базаи худ захира мекунем - ҷадвали қаблан сохташуда. Ин аст, ки ҳангоми боркунии файл, ин an object барои мо хеле муфид хоҳад буд ва ҳангоми нест кардан мо набояд онро ба ҳеҷ ваҷҳ фаромӯш кунем. Биёед ин ҷадвалро бубинем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 3Ва биёед id-ро ба мисли калонсолон AUTO_INCREMENT муқаррар кунем, то дар сатҳи пойгоҳи додаҳо идентификатор ба таври худкор тавлид шавад. Аввалан, биёед ба сохтори худ назар андозем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 4Объект дар ҷадвали дар боло нишон додашуда:
@Builder(toBuilder = true)
@Getter
@ToString
public class FileInfo {

   private Long id;

   private String name;

   private Long size;

   private String key;

   private LocalDate uploadDate;
}

Бор кунед

Биёед ба контроллер назар андозем:
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
public class FileController {

   private final FileService fileService;

   @PostMapping
   public ResponseEntity<FileInfo> upload(@RequestParam MultipartFile attachment) {
       try {
           return new ResponseEntity<>(fileService.upload(attachment), HttpStatus.CREATED);
       } catch (IOException e) {
           return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
       }
   }
Чизҳои ҷолиб: 9 - Мо файлро дар шакли қабул мекунем MultipartFile. Он инчунин метавонад ҳамчун массиви byteҳо қабул карда шавад, аммо ба ман ин хосият бештар маъқул аст, зеро мо метавонем MultipartFileхосиятҳои гуногуни файли интиқолшударо истихроҷ кунем. 10 - 14 - мо амалҳои худро ҷамъ меорем try catch, ки агар истисно дар сатҳи поёнтар рух диҳад, мо онро баландтар мефиристем ва ҳамчун ҷавоб 400 хато мефиристем. Минбаъд сатҳи хидматрасонӣ аст:
public interface FileService {

   FileInfo upload(MultipartFile resource) throws IOException;
Биёед ба татбиқи он назар андозем:
@Service
@RequiredArgsConstructor
public class FileServiceImpl implements FileService {

   private final FileDAO fileDAO;
   private final FileManager fileManager;

   @Transactional(rollbackFor = {IOException.class})
   @Override
   public FileInfo upload(MultipartFile resource) throws IOException {
       String key = generateKey(resource.getName());
       FileInfo createdFile = FileInfo.builder()
               .name(resource.getOriginalFilename())
               .key(key)
               .size(resource.getSize())
               .build();
       createdFile = fileDAO.create(createdFile);
       fileManager.upload(resource.getBytes(), key);

       return createdFile;
   }
8 - дар сурати IOException, ҳамаи захираҳои мо дар пойгоҳи додаҳо баргардонида мешаванд. 11 - мо калидеро тавлид мекунем, ки ҳангоми захира кардани файл барои файл беназир хоҳад буд (ҳатто агар ду файл бо як ном захира карда шаванд ҳам, нофаҳмиҳо вуҷуд нахоҳад дошт). 12 — мо an objectеро месозем, ки дар базаи маълумот захира кунем. 17 — мо an objectро бо маълумот ба базаи маълумот меронем. 18 - файлро бо номи ҳашшуда захира кунед. 20 — мо an objectи сохташударо бармегардонем FileInfo, аммо бо идентификатсияи тавлидшуда дар базаи маълумот (ин дар поён баррасӣ хоҳад шуд) ва санаи офариниш. Усули тавлиди калиди файл:
private String generateKey(String name) {
   return DigestUtils.md5Hex(name + LocalDateTime.now().toString());
}
Дар ин ҷо мо ном + санаи офаринишро ҳаш мекунем, ки беназирии моро таъмин мекунад. Интерфейси қабати Дао:
public interface FileDAO {

   FileInfo create(FileInfo file);
Татбиқи он:
@Repository
@RequiredArgsConstructor
public class FileDAOImpl implements FileDAO {

   private static final String CREATE_FILE = "INSERT INTO files_info(file_name, file_size, file_key, upload_date) VALUES (?, ?, ?, ?)";

   private final JdbcTemplate jdbcTemplate;

   @Override
   public FileInfo create(final FileInfo file) {
       LocalDate uploadDate = LocalDate.now();
       GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
       jdbcTemplate.update(x -> {
           PreparedStatement preparedStatement = x.prepareStatement(CREATE_FILE, Statement.RETURN_GENERATED_KEYS);
           preparedStatement.setString(1, file.getName());
           preparedStatement.setLong(2, file.getSize());
           preparedStatement.setString(3, file.getKey());
           preparedStatement.setDate(4, Date.valueOf(uploadDate));
           return preparedStatement;
       }, keyHolder);

       return file.toBuilder()
               .id(keyHolder.getKey().longValue())
               .uploadDate(uploadDate)
               .build();
   }
11 - санаеро эҷод кунед, ки мо онро захира мекунем. 12 - 21 - мо an objectро захира мекунем, аммо ба таври мураккабтар бо эҷоди возеҳи an object, PreparedStatementто id-и тавлидшуда берун карда шавад (он онро на бо дархости алоҳида, балки дар шакли метамаълумоти посух берун мекунад ). 22 - 26 - мо сохтмони an objectи деринаи худро ба итмом мерасонем ва онро ба боло медиҳем (воқеан, вай онро ба итмом намерасонад, балки an objectи нав месозад, майдонҳои интиқолшударо пур мекунад ва боқимондаашро аз асл нусхабардорӣ мекунад) . Биёед бубинем, ки чӣ тавр файлҳои мо дар FileManager:
public void upload(byte[] resource, String keyName) throws IOException {
   Path path = Paths.get(DIRECTORY_PATH, keyName);
   Path file = Files.createFile(path);
   FileOutputStream stream = null;
   try {
       stream = new FileOutputStream(file.toString());
       stream.write(resource);
   } finally {
       stream.close();
   }
}
1 — мо файлро ҳамчун массиви byteҳо ва алоҳида номеро, ки таҳти он захира карда мешавад, қабул мекунем (калиди тавлидшудаи мо). 2 - 3 - роҳ эҷод кунед (ва дар роҳ мо роҳро бо иловаи калиди худ менависем) ва файлро дар баробари он эҷод кунед. 6 - 7 - ҷараён эҷод кунед ва byteҳои моро дар он ҷо нависед (ва ҳамаи ин чизҳоро печонед, try-finallyто боварӣ ҳосил кунед, ки ҷараён ҳатман баста мешавад). Аммо, бисёре аз усулҳо метавонанд IOException-ро партоянд. Дар ин ҳолат, ба шарофати интиқоли дар сарлавҳаи усул нишондодашуда, мо онро ба контроллер интиқол медиҳем ва ҳолати 400 медиҳем. Биёед ҳама чизро дар Postman санҷем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 5Тавре ки шумо мебинед, ҳама чиз хуб аст, ҷавоб 201 аст, ҷавоби JSON дар шакли як сохтори устувори мо дар пойгоҳи додаҳо омадааст ва агар мо ба анбори худ назар андозем: DB: мо Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 6мебинем, ки мо арзиши нав дорем. (=*;*=)

Зеркашӣ кунед

Назоратчӣ:
@GetMapping(path = "/{id}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> download(@PathVariable("id") Long id) {
   try {
       FileInfo foundFile = fileService.findById(id);
       Resource resource = fileService.download(foundFile.getKey());
       return ResponseEntity.ok()
               .header("Content-Disposition", "attachment; filename=" + foundFile.getName())
               .body(resource);
   } catch (IOException e) {
       return new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }
}
Мо инчунин онро мепӯшем try-catchва дар ҳолати IOException мо ҳолати посухи 404 мефиристем (наёфтанд). 4 - an objectи ҳамсояи FileInfo аз пойгоҳи додаҳо берун кунед. 5 - бо истифода аз калид аз an object, файлро зеркашӣ кунед 6-8 - файлро баргардонед ва номи файлро ба сарлавҳа илова кунед (аз нав, аз субъект бо маълумот дар бораи файл гирифта шудааст). Биёед амиқтар назар андозем. Интерфейси хидматрасонӣ:
Resource download(String key) throws IOException;

FileInfo findById(Long fileId);
Татбиқи:
@Override
public Resource download(String key) throws IOException {
   return fileManager.download(key);
}

@Transactional(readOnly = true)
@Override
public FileInfo findById(Long fileId) {
   return fileDAO.findById(fileId);
}
Дар ин ҷо ҳеҷ чизи ҷолибе вуҷуд надорад: усули ҷустуҷӯи an object аз рӯи ID ва зеркашии файл, ба истиснои 46 - мо қайд мекунем, ки мо транзаксия барои хондан дорем. Сатҳи Дао:
FileInfo findById(Long fileId);
Татбиқи:
private static final String FIND_FILE_BY_ID = "SELECT id, file_name, file_size, file_key, upload_date FROM files_info WHERE id = ?";

@Override
public FileInfo findById(Long fileId) {
   return jdbcTemplate.queryForObject(FIND_FILE_BY_ID, rowMapper(), fileId);
}

private RowMapper<FileInfo> rowMapper() {
   return (rs, rowNum) -> FileInfo.builder()
           .id(rs.getLong("id"))
           .name(rs.getString("file_name"))
           .size(rs.getLong("file_size"))
           .key(rs.getString("file_key"))
           .uploadDate(rs.getObject("upload_date", LocalDate.class))
           .build();
}
4 — ҷустуҷӯ аз рӯи ID бо истифода jdbcTemplateаз ва RowMapper. 8 - 15 - татбиқ RowMapperбарои ҳолати мушаххаси мо, барои муқоисаи маълумот аз пойгоҳи додаҳо ва майдонҳои модел. Биёед биравем FileManagerва бубинем, ки файли мо чӣ гуна бор карда мешавад:
public Resource download(String key) throws IOException {
   Path path = Paths.get(DIRECTORY_PATH + key);
   Resource resource = new UrlResource(path.toUri());
   if (resource.exists() || resource.isReadable()) {
       return resource;
   } else {
       throw new IOException();
   }
}
Мо файлро ҳамчун an object бармегардонем Resourceва мо тавассути калид ҷустуҷӯ хоҳем кард. 3 - Resourceдар баробари роҳ + калид эҷод кунед. 4 - 8 — мо тафтиш мекунем, ки файл дар роҳи додашуда холӣ нест ва онро мехонем. Агар ҳама чиз хуб бошад, мо онро бармегардонем ва агар не, мо IOException-ро ба боло мепартоем. Биёед усули худро дар Postman тафтиш кунем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 7Тавре ки шумо мебинед, он хуб кор кард))

Нобуд кунед

@DeleteMapping(value = "/{id}")
public ResponseEntity<Void> delete(@PathVariable("id") Long id) {
   try {
       fileService.delete(id);
       return new ResponseEntity<>(HttpStatus.OK);
   } catch (IOException e) {
       return new ResponseEntity<>(HttpStatus.NOT_FOUND);
   }
}
Дар ин ҷо ҳеҷ чизи махсусе нест: мо инчунин 404-ро дар сурати нокомии истифодаи try-catch. Интерфейси хидматрасонӣ:
void delete(Long fileId) throws IOException;
Татбиқи:
@Transactional(rollbackFor = {IOException.class})
@Override
public void delete(Long fileId) throws IOException {
   FileInfo file = fileDAO.findById(fileId);
   fileDAO.delete(fileId);
   fileManager.delete(file.getKey());
}
1 - инчунин баргардонидани тағироти маълумот (нест кардан) ҳангоми рух додани IOException. 5 - нест кардани маълумот дар бораи файл аз пойгоҳи додаҳо. 6 - худи файлро аз "захираи" мо нест кунед. интерфейси dao:
void delete(Long fileId);
Татбиқи:
private static final String DELETE_FILE_BY_ID = "DELETE FROM files_info WHERE id = ?";

@Override
public void delete(Long fileId) {
   jdbcTemplate.update(DELETE_FILE_BY_ID, fileId);
}
Ҳеҷ чиз ба ин монанд нест - танҳо нест кунед. Худи файлро нест кунед:
public void delete(String key) throws IOException {
       Path path = Paths.get(DIRECTORY_PATH + key);
       Files.delete(path);
   }
}
Мо Postman-ро истифода мебарем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар пойгоҳи додаҳо - 8Мо дар анбор нигоҳ мекунем: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 9Холӣ :) Ҳоло дар база: Нигоҳ доштани файлҳо дар барнома ва маълумот дар бораи онҳо дар базаи маълумот - 10Мо мебинем, ки ҳама чиз хуб аст))

Санҷиш

Биёед кӯшиш кунем, ки барои мо тест нависем FileManager. Аввалан, биёед ба сохтори қисми санҷиш назар андозем: mockFile.txt файлест, ки мо бо он амалиёти худро бо нигоҳдории файл месанҷем. testFileStorage ивазкунандаи нигаҳдории мо хоҳад буд. FileManagerTest:
public class FileManagerTest {

   private static MultipartFile multipartFile;

   private static FileManager manager;

   private static FileInfo file;

   @BeforeClass
   public static void prepareTestData() throws IOException {
       file = FileInfo.builder()
               .id(9L)
               .name("mockFile.txt")
               .key("mockFile.txt")
               .size(38975L)
               .uploadDate(LocalDate.now())
               .build();
       multipartFile = new MockMultipartFile("mockFile", "mockFile.txt", "txt",
               new FileInputStream("src/test/resources/mockFile.txt"));
       manager = new FileManager();
   }
Дар ин ҷо мо супориши маълумоти санҷиширо мебинем. Санҷиши сарфаи файл:
@Test
public void uploadTest() throws IOException {
   ReflectionTestUtils.setField(manager, "DIRECTORY_PATH", "src/test/resources/testFileStorage/");

   manager.upload(multipartFile.getBytes(), "mockFile.txt");

   Path checkFile = Paths.get("src/test/resources/testFileStorage/mockFile.txt");
   assertThat(Files.exists(checkFile)).isTrue();
   assertThat(Files.isRegularFile(checkFile)).isTrue();
   assertThat(Files.size(checkFile)).isEqualTo(multipartFile.getSize());
   Files.delete(checkFile);
}
3 — бо истифода аз инъикоси санҷиш, мо доимии худро дар хидмат тағир медиҳем, то роҳи захира кардани файлро таъин кунем. 5 — усули озмудашавандаро даъват кунед. 7 - 10 — дуруст ичро шудани сарфаро месанчем. 11 - файли захирашударо нест кунед (мо набояд ягон пайро тарк кунем).
@Test
public void downloadTest() throws IOException {
   ReflectionTestUtils.setField(manager, "DIRECTORY_PATH", "src/test/resources/");

   Resource resource = manager.download(file.getKey());

   assertThat(resource.isFile()).isTrue();
   assertThat(resource.getFilename()).isEqualTo(file.getName());
   assertThat(resource.exists()).isTrue();
}
Санҷиши боркунии файл: 3 - боз, роҳи моро барои FileManager. 5 - усули санҷидашударо истифода баред. 7 - 9 — натичаи ичроро тафтиш кунед. Санҷиши тозакунии файл:
@Test
public void deleteTest() throws IOException {
   Path checkFile = Paths.get("src/test/resources/testFileStorage/mockFile.txt");
   Files.createFile(checkFile);
   assertThat(Files.exists(checkFile)).isTrue();
   assertThat(Files.isRegularFile(checkFile)).isTrue();
   ReflectionTestUtils.setField(manager, "DIRECTORY_PATH", "src/test/resources/testFileStorage/");

   manager.delete(file.getKey());

   assertThat(Files.notExists(checkFile)).isTrue();
}
9 - 3 - 4 - роҳро таъин кунед ва файл эҷод кунед. 5 - 6 - мавҷудияти онро тафтиш мекунем. 9 - мо усули тафтишшавандаро истифода мебарем. 77 - мо тафтиш мекунем, ки an object дигар нест. Ва биёед бубинем, ки мо аз ҷиҳати вобастагӣ чӣ дорем:
<dependencies>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jdbc</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <scope>test</scope>
       <exclusions>
           <exclusion>
               <groupId>org.junit.vintage</groupId>
               <artifactId>junit-vintage-engine</artifactId>
           </exclusion>
       </exclusions>
   </dependency>
   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.10</version>
       <scope>provided</scope>
   </dependency>
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
       <version>8.0.18</version>
   </dependency>
   <dependency>
       <groupId>commons-codec</groupId>
       <artifactId>commons-codec</artifactId>
       <version>1.9</version>
   </dependency>
   <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.13-rc-2</version>
       <scope>test</scope>
   </dependency>
</dependencies>
Ин ҳама барои ман имрӯз аст))
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION