JavaRush /جاوا بلاگ /Random-SD /فائلن کي ايپليڪيشن ۾ محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ڏ...

فائلن کي ايپليڪيشن ۾ محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ڏانهن

گروپ ۾ شايع ٿيل
ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 1 اچو ته تصور ڪريو ته توهان پنهنجي ويب ايپليڪيشن تي ڪم ڪري رهيا آهيو. منهنجي مضمونن ۾ آئون هن موزاڪ جي انفرادي ٽڪرن کي ڏسان ٿو، جهڙوڪ: اهي موضوع ڪيئن مفيد آهن؟ ۽ حقيقت اها آهي ته اهي مثال حقيقي منصوبن تي ڪم ڪرڻ جي تمام ويجهو آهن، ۽ انهن عنوانن کي جانچڻ توهان لاء تمام مفيد ثابت ٿيندو. اڄ اسان هن موزيڪ جو ايندڙ ٽڪرو کڻنداسين - فائلن سان ڪم ڪندي، ڇو ته اڄڪلهه توهان هاڻي هڪ سائيٽ نه ڳولي سگهو ٿا جيڪو انهن سان رابطو نه ڪندو آهي (مثال طور، سڀني قسمن جا ويب دڪان، سماجي نيٽ ورڪ، وغيره). جائزو ورتو ويندو اپلوڊ/ڊائون لوڊ/ڊليٽ طريقن کي استعمال ڪندي مثال طور ؛ اسان ان کي فولڊر ۾ محفوظ ڪنداسين (وسيع ۾) اسان جي ايپليڪيشن ۾، جيئن ان کي پيچيده نه ٿئي. ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 2خيال اهو آهي ته اسان محفوظ ڪنداسين، فائل کان علاوه اسان جي نام نهاد فائل اسٽوريج ۾، اسان جي ڊيٽابيس ۾ اسان جي فائل (سائيز، نالو، وغيره) بابت معلومات سان گڏ هڪ ادارو - هڪ اڳ ۾ ٺهيل ٽيبل. اهو آهي، جڏهن هڪ فائل لوڊ ڪندي، اهو ادارو اسان لاء تمام گهڻو ڪارائتو ٿيندو، ۽ حذف ڪرڻ وقت، اسان کي ان جي باري ۾ ڪنهن به طريقي سان وسارڻ نه گهرجي. اچو ته هن جدول تي هڪ نظر رکون: ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 3۽ اچو ته سڃاڻپ کي سيٽ ڪريون 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. اهو بائيٽ جي هڪ صف جي طور تي پڻ حاصل ڪري سگهجي ٿو، پر مون کي هي اختيار بهتر آهي، ڇاڪاڻ ته اسان 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 — اسان ڊيٽابيس ۾ محفوظ ڪرڻ لاءِ هڪ ادارو ٺاهيندا آهيون. 17 — اسان اداري کي معلومات سان گڏ ڊيٽابيس ۾ هلائيندا آهيون. 18 - هيش ٿيل نالي سان فائل محفوظ ڪريو. 20 — اسان ٺاهيل ادارو واپس ڪريون ٿا FileInfo، پر ڊيٽابيس ۾ ٺاهيل id سان (هي هيٺ بحث ڪيو ويندو) ۽ ٺاھڻ جي تاريخ. ھڪڙي فائل کي چاٻي پيدا ڪرڻ جو طريقو:
private String generateKey(String name) {
   return DigestUtils.md5Hex(name + LocalDateTime.now().toString());
}
هتي اسان نالو + ٺاھڻ جي تاريخ کي هٽايو، جيڪو اسان جي انفراديت کي يقيني بڻائيندو. Dao پرت انٽرفيس:
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 - اسان انٽيٽي کي محفوظ ڪريون ٿا، پر وڌيڪ پيچيده انداز ۾، اعتراض جي واضح ٺاھڻ سان، PreparedStatementجيئن پيدا ٿيل آئي ڊي کي ڪڍي سگھجي ٿو (اھو ان کي ڌار ڌار درخواست سان نه، پر ردعمل ميٽا ڊيٽا جي صورت ۾ ڪڍي ٿو. ). 22 - 26 - اسان پنهنجي ڊگهي تڪليف واري اداري جي تعمير کي مڪمل ڪريون ٿا ۽ ان کي مٿي ڏيون ٿا (حقيقت ۾، هو ان کي مڪمل نه ڪندو آهي، پر هڪ نئين شئي ٺاهي، منتقل ٿيل فيلڊ کي ڀرڻ ۽ باقي اصل کان نقل ڪري ٿو) . اچو ته ڏسون ته اسان جون فائلون ڪيئن محفوظ ٿينديون 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 - اسان فائل قبول ڪريون ٿا بائٽس جي هڪ صف طور ۽ الڳ الڳ نالو جنهن جي تحت ان کي محفوظ ڪيو ويندو (اسان جي ٺاهيل ڪيئي). 2 - 3 - ھڪڙو رستو ٺاھيو (۽ رستي ۾ اسين رستو لکون ٿا ۽ اسان جي ڪيئي) ۽ ان سان گڏ ھڪڙي فائل. 6 - 7 - ھڪڙو وهڪرو ٺاھيو ۽ اتي اسان جا بائيٽ لکو (۽ اھو سمورو سامان ان ۾ ويڙھيو try-finallyته پڪ ڪريو ته وهڪرو ضرور بند ٿيندو). بهرحال، ڪيترائي طريقا IOException اڇلائي سگھن ٿا. انهي صورت ۾، طريقي جي هيڊر ۾ بيان ڪيل اڳتي وڌڻ جي مهرباني، اسان ان کي ڪنٽرولر ڏانهن منتقل ڪنداسين ۽ 400 جي حيثيت ڏينداسين. اچو ته پوسٽمن ۾ سڄي شيء کي جانچيو: ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 5جيئن توهان ڏسي سگهو ٿا، سڀ ڪجهه ٺيڪ آهي، جواب 201 آهي، جواب JSON ڊيٽابيس ۾ اسان جي مسلسل اداري جي صورت ۾ آيو، ۽ جيڪڏهن اسان اسان جي اسٽوريج ۾ ڏسون ٿا: ڊي بي: ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 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 - ڊيٽابيس مان ڀرسان FileInfo ادارو ڪڍيو. 5 - اداري مان ڪي استعمال ڪندي، فائل ڊائون لوڊ ڪريو 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);
}
هتي ڪجھ به خاص دلچسپ ناهي: 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();
   }
}
اسان فائل کي هڪ اعتراض جي طور تي واپس ڪريون ٿا Resource، ۽ اسان کي ڳوليندا سين. 3 - Resourceرستي سان ٺاهيو + ڪيچ. 4 - 8 - اسان چيڪ ڪريون ٿا ته ڏنل رستي تي فائل خالي نه آهي ۽ ان کي پڙهو. جيڪڏهن سڀ ڪجهه ٺيڪ آهي، اسان ان کي واپس ڪريون ٿا، ۽ جيڪڏهن نه، اسان مٿي تي IOException اڇليندا آهيون. اچو ته پوسٽمن ۾ اسان جو طريقو چيڪ ڪريو: ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 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);
   }
}
اسان پوسٽمن استعمال ڪندا آهيون: ايپليڪيشن ۾ فائلون محفوظ ڪرڻ ۽ انهن بابت ڊيٽا ڊيٽابيس ۾ - 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 - اسان چيڪ ڪريون ٿا ته اعتراض هاڻي اتي ناهي. ۽ اچو ته ڏسون ته اسان وٽ انحصار جي لحاظ کان ڇا آهي:
<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